changeset 3807:d73c43798a99 pre2.6-2011-11-04

merged flys-backend/pre2.6-2011-11-04
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:51 +0200
parents 881fcd01e056 (diff) 9df06b88c079 (current diff)
children f68d6dcaea94
files
diffstat 584 files changed, 87363 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:51 2012 +0200
@@ -0,0 +1,8089 @@
+2011-11-08  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix flys/issue406 (Themestyle-editor: themes for "other.wkms" and
+	"other.wqkms" missing)
+
+	* doc/conf/themes.xml: Fixed typos in WKms and WQKms theme names.
+
+2011-11-08  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix flys/issue405 (Datacage: Recommendations get loaded twice).
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java
+	  (setup): Do not try/catch exception.
+	  (spawn_state): Generate just one "general" output.
+
+2011-11-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data.xml: Removed orphaned datacage configuration which is
+	  no longer loadable.
+
+2011-11-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Create
+	  line and polygon layers for barriers only if they are really existing.
+
+2011-11-04  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Disable mainvalue-recommendations for discharge curves as they trigger
+	a yet-to-be understood NPE.
+
+	* doc/conf/meta-data.xml: Comment mainvalue-recommendations for
+	  discharge curves.
+
+2011-11-04  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added new matching condition for theme-mappings: the name of
+	the output.
+
+	* src/main/java/de/intevation/flys/themes/ThemeMapping.java:
+	  Added output field and function to match it against a given
+	  output name.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Createing ThemeMapping with output attribute from configuration.
+	  
+	* src/main/java/de/intevation/flys/themes/ThemeFactory.java:
+	  (getTheme(FLYSContext, string)): Removed, never called.
+	  (getTheme): Added outputName argument, match it.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Pass outputs name until it can be matched against mapping.
+
+2011-11-04  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* doc/conf/themes.xml: Added default themes for other.w(q)kms.
+
+2011-11-04  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Cosmetics, reduce logging noise.
+
+2011-11-03  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added access to static W_Q_Kms - data in much the same way then static
+	WKms.
+
+	* src/main/java/de/intevation/flys/artifacts/model/StaticWQKmsCacheKey.java:
+	  Cache Key for static wqkms data.
+	
+	* src/main/java/de/intevation/flys/artifacts/model/WQKmsFacet.java:
+	  Facet for WQKms.
+	
+	* src/main/java/de/intevation/flys/artifacts/model/WQKmsFactory.java:
+	  Factory to access WQKms.
+	
+	* src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java:
+	  Artifact that provides 'static' WQKms.
+	
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added STATIC_WQKMS type.
+
+2011-11-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/XYDatasetToZeroMapper.java:
+	  New. Maps series to zero to be compatible with XYDifferenceRenderer.
+	  It returns an iterator over XYDatasets to enable splitting by NaNs,
+	  which still needs to be implemented.
+
+	* src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java:
+	  Uses a XYDatasetToZeroMapper now. Fixed package name.
+
+2011-11-03  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added new matching options for theme-mappings to allow more
+	overspecification of defaults (e.g. now name,description-pattern and
+	master-artifacts attributes are matched). The first full match from
+	the configuration file is done.
+	New matching option in the masterAttr- field of a mapping are
+	super-basic until new use-cases come up.
+	Concrete new themes are point-styles of Ws when locations where chosen
+	to calculate.
+
+	* doc/conf/themes.xml: Added newly defined Themes.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Put master-artifact in flys-context.
+
+	* src/main/java/de/intevation/flys/themes/ThemeMapping.java:
+	  Accept masterAttr in constructor.
+	  (masterAttrMatches): New, check masterAttr-condition against
+	  artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  When creating ThemeMappings, pass in masterAttr.
+
+	* src/main/java/de/intevation/flys/themes/ThemeFactory.java:
+	  (getTheme): Evaluate masterAttr-condition, always return first full
+	  match.
+
+2011-11-03  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java:
+	  Added ARTIFACT key, documentation.
+
+2011-11-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java:
+	  New. At the moment a pure copy of JFreeChart's XYDifferenceRenderer.
+	  Needs to be refactored to cope with its limitations:
+
+	  - Series numbers need to be zero based. We have more than two series
+	    in our diagrams.
+
+	  - Cannot handle definition holes indicated by NaNs. We have these
+	    cases e.g. more than one "Fliessbereich".
+
+2011-11-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  Reenabled dumping data to disk.
+
+2011-11-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Removed superfluous import.
+
+2011-11-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java:
+	  Use new pair in stack of results and connections because they are
+	  always used in pairs. Maintaining two separate stacks is not
+	  needed any longer.
+
+2011-11-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+	
+	* src/main/java/de/intevation/flys/utils/Pair.java: New. A generic pair.
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  Rewritten to useful as test bed for "Raum/Flaeche" operations.
+
+2011-11-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: Added
+	  setter methods for all parameters and removed the constructors. There is
+	  just an empty constructor - all parameters need to be set via setter
+	  methods.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Modified the
+	  creation of LayerInfo objects.
+
+	* src/main/java/de/intevation/flys/artifacts/model/DBLayerInfo.java:
+	  Removed, because the internal structure and constructors of LayerInfo have
+	  changed, so that we gonna use LayerInfo for all layers now.
+
+	* doc/conf/themes.xml: Modified the color definitions of
+	  'floodmap.riveraxis' and 'floodmap.kms' themes and added a 'symbol' field
+	  to 'floodmap.kms'.
+
+	* doc/conf/mapserver/symbols.sym,
+	  doc/conf/mapserver/fontset.txt: New. Required by Mapserver.
+
+	* doc/conf/mapserver/db_layer.vm: Added an 'EXTENT' field that is filled
+	  using LayerInfo.getExtent().
+
+	* doc/conf/mapserver/mapfile.vm: Modified FONTSET directory and added a
+	  SYMBOLSET.
+
+	* src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java: Added an
+	  abstract method getGeometryType().
+
+	* src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java: Override
+	  getGeometryType() of WMSDBArtifact. This Artifact provides "POINT"s.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Create new WMSDBLayerFacets with geometry type "LINE".
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSDBLayerFacet.java:
+	  Added a 'geometryType' attribute and getter/setter methods. This attribute
+	  determines the type of geometry provided by this database wms layer. Types
+	  could be "POLYGON", "POINT", "LINE" and so on.
+
+	* src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java:
+	  Added a 'symbol' attribute to the inner class Clazz.
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java: Added a function to
+	  parse the symbol field of a theme. This symbol is used for
+	  MapserverStyle.Clazz.
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Apply point theme to heightmarks when imported in longitudinal
+	section diagram.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added new FacetType HEIGHTMARK_POINTS.
+	
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java:
+	  Allow name to be given in constructor.
+	
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java:
+	  If heightmarks were loaded, give respective name in WKmsFacet
+	  generation.
+
+	* doc/conf/themes.xml: Added virtual "Points" and concrete
+	  heightmark_points - theme.
+	
+	* doc/conf/artifacts/winfo.xml: Made longitudinal_section output
+	  compatible with heightmarks_points.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Render heightmarks like other wkms.
+	
+	* doc/conf/meta-data.xml: Changed heightmark ids such that it can be
+	  identified in StaticWKmsFacet .
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java:
+	  Documentation added.
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java:
+	  Fix bug where (wrong) active-attribute was set, doc.
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Refactoring to allow mainvalues in both discharge and computed
+	discharge curve diagrams.
+
+	* src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java
+	(addPointsQW): New helper function.
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Moved doMainValueQAnnotations, doMainValueWAnnotations from child-
+	  to parent-class, extracetd doDischargeOut. Use
+	  StyledSeriesBuilder.addPointsQW .
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Removed logger/debugging noise.
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	1) Pass outputs name to artifacts getInitialFacetActivity().
+	2) Do not allow "gaps" in positions of facets in outputs in attributes
+	   of collection (prevent e.g. positions 1,3,5; will become 1,2,3
+	   instead).
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	  Pass outputname to artifacts getInitialFacetActivity(), prevent
+	  gaps in facets positions in outputs (1,3,10 become 1,2,3).
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	1) Give Artifacts information about the out when they have to decide
+	whether a given facet is initially in/active by adding parameter
+	to getInitialFacetActivity(+outputName).
+	2) Generate separate Set of MainValueFacets for discharge curves.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  (getInitialFacetActivity): Adjusted, new parameter outputName.
+	  Added MAINVALUES_{Q,W} definition.
+
+	* src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java:
+	  Accept name in constructor.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Create second pair of MainValuesFacets, give distinguishable names.
+
+2011-11-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added compatibility of (computed)discharge-curve diagrams with
+	mainvalues.
+
+	* doc/conf/artifacts/winfo.xml: Added mainvalues to compatibility list
+	  of (computed) discharge curve outputs. These facet-definitionss can
+	  differ in names because we can can have up to 4 mainvalue facets in
+	  one state.
+
+2011-11-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java: Determine
+	  the extent of such WMS layer based on the list of RiverAxisKm objects
+	  returned by the backend.
+
+2011-11-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data.xml: Added a datacage node that represents a
+	  kilometer WMS layer.
+
+	* doc/conf/conf.xml: Added a new Artifact WMSKmArtifact.
+
+	* doc/conf/themes.xml: Added a style for 'floodmap.kms' facets.
+
+	* doc/conf/artifacts/winfo.xml: Defined 'floodmap.kms' as valid floodmap
+	  facet.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added
+	  a 'floodmap.kms' facet.
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a method to
+	  determine the srid of a river based on its name.
+
+	* src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java: New. This
+	  Artifact should act as base Artifact for WMS layers that represent data
+	  from database datastore.
+
+	* src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java: New. This
+	  Artifact is used to generate facets for kilometer WMS layers.
+
+	* 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 default descriptions
+	  for 'floodmap.kms' facets.
+
+2011-11-01	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix incompilability.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java:
+	  Adjust to changed CrossSectionFacet.
+
+2011-11-01	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Minor preparations to handle multiple cross sections in one
+	diagram/artifact, faking certain aspects (e.g. ability to display
+	multiple cross sections, but let these fetch the exactly same data
+	for now).
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Parameterize but fake access to cross-section (always take first
+	  one).
+	  (getCrossSectionName,getCrossSectionNames): Renamed, access names
+	  of all cross-sections, so that at least facets with different names
+	  are created (they will still deliver the same data).
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java:
+	  Allow indexing.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Index created cross-sections.
+
+2011-11-01	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Show multiple water lines and facets in cross-section diagram if
+	multiple waterlevel values had been entered.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java
+	  (appendBackgroundActivity): Made static.
+	  (getWaterLines): Add 'idx' argument to specify index of queried
+			   waterlevel.
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java:
+	  Add index.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Add one Facet for each of the computed waterlevels.
+
+2011-10-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java:
+	  Added code to calculate intersection points.
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java:
+	  Added polygons for trivial cases. WIP
+
+2011-10-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java:
+	  Made it compilable again.
+
+2011-10-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java:
+	  Made X() and Y() access macros public.
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java:
+	  More code. WIP.
+
+2011-10-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java:
+	  Handle start points when building polygons. Work in progress.
+
+2011-10-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java:
+	  New. Vector operations on Point2D.
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java:
+	  Moved vector operations to VectorUtils.
+
+2011-10-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java:
+	  Do not call size() in for loop again and again.
+
+2011-10-30	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java:
+	  Interim check in. Work in progress.
+
+2011-10-28	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: New.
+	  Polygon class to help creating "Raum/Flaeche" renderers with gaps in
+	  their definitions. WORK IN PROGRESS!
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/themes.xml: Added a default theme for the riveraxis used in the
+	  floodmap.
+
+	* src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java:
+	  New. This class is used by ThemeUtil to create a style which is
+	  compatible for Mapserver-
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java: Added a method to
+	  retrieve a Mapserver compatible style (as string) based on a given
+	  Document (that comes from CollectionItem's attribute).
+
+	* src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java:
+	  Implemented the setStyle() and getStyle() methods.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Added a
+	  new parameter 'style' to createDatabaseLayer(). This parameter is set on
+	  LayerInfo.
+
+	* src/main/java/de/intevation/flys/exports/MapGenerator.java: Create
+	  Mapserver compatible styles and call createDatabaseLayer() with this
+	  style.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Defined "floodmap.riveraxis" and
+	  "floodmap.wmsbackground" as compatible layers for the floodmap output.
+
+	* doc/conf/mapserver/db_layer.vm: New. This layer template is used for
+	  Mapserver layers with database datastore.
+
+	* doc/conf/mapserver/dbconnection.include: New. The database configuration
+	  used in the db_layer template.
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a method
+	  getUserWMSUrl() that returns the URL to the user specific WMS server.
+	  This method requires a UUID of an Artifact to identify the owner of the
+	  Artifact.
+
+	* src/main/java/de/intevation/flys/wsplgen/FacetCreator.java: Use
+	  FLYSUtils.getUserWMSUrl() to create the URL to the user WMS for
+	  WMSLayerFacets creation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSDBLayerFacet.java:
+	  New. Subclasses WMSLayerFacet to save data and filter parameters used
+	  for database storage in Mapfiles.
+
+	* src/main/java/de/intevation/flys/artifacts/model/DBLayerInfo.java: New.
+	  Subclasses LayerInfo to save database relevant parameters.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Creates new WMSDBLayerFacets, so that the riveraxis layer data is
+	  fetched from database.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Added a
+	  public method that allows creating layers (type LINE) based on
+	  WMSDBLayerFacets.
+
+	* src/main/java/de/intevation/flys/exports/MapGenerator.java: Enabled
+	  support for Facets other than "floodmap.wsplgen" and "floodmap.barriers".
+	  Those other Facets are supposed to be WMSDBLayerFacets.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Refactored, added StyledSeriesBuilder to unify adding points to
+	XYSeries.
+
+	* src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java:
+	  New class to help with adding points to XYSeries.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Use StyledSeriesBuilder to add points to series.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added DC-conf, such that static data can be loaded from w-difference
+	diagrams datacage.
+
+	* doc/conf/meta-data.xml: Minor "refactoring" (definition of two
+	  macros, allow certain static data to be loaded via datacage to
+	  w-difference diagrams, too.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Load and display annotations in w-differences, minor polishing.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Use FacetType 'instead' of string. Allow Annotations in
+	  WDifferences- diagram.
+
+	* doc/conf/meta-data.xml: Recommend annotations in w-differences case.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java: 
+	  Survive case where a given output doesnt exist in compatibility
+	  matrix.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/StaticState.java:
+	  Added simpler constructor.
+	
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Adjusted construction of StaticStates.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Allow Annotations in longitudinal and
+	  w-differences diagrams.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Use artifacts configuration (e.g. winfo.xml) to define which facets
+	can be used in which output. Hide no-matches.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java,
+	  src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Added use of "compatibility matrix". Only include facets in in
+	  collections description document that are marked compatible in the
+	  masterartifacts configuration (e.g. winfo.xml).
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java
+	  (mergeAttributes, getMasterArtifact): Extraced, updated caller.
+	  Cosmetics to reduce indentation one step.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java
+	  (getStateHistoryIds): New, return list of current and all previous
+	  state ids.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Trigger the re-creation of FLYS mapfile if endOfLife() of this state is
+	  called.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/mapserver/mapfile.vm: The "layers" injected by VelocityEngine is
+	  now used to include layers. A single string in this list represents the
+	  path to a file which contains a LAYER section for Mapserver' Mapfile.
+
+	* src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Removed the
+	  update() call of MapfileGenerator. Mapfiles are generated by MapGenerator
+	  only which requires a FLYSArtifactCollection.doOut()!
+
+	* src/main/java/de/intevation/flys/exports/MapGenerator.java: Call update()
+	  of MapfileGenerator to trigger the re-creation of mapfile(s).
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Collect all
+	  LAYER snippets from filesystem and inject the filepath for each snippet
+	  into the Mapfile template.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java,
+	  src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Cosmetics, doc.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Let OutputParser and AttributeParser collect all facets on the way.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java,
+	  src/main/java/de/intevation/flys/collections/AttributeParser.java:
+	  Collect all facets while iterating over Outputs and Attributes,
+	  documentation added.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Extracted getFlysContext from FLYSArtifacts into FLYSUtils.
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java
+	  (getFlysContext): Added, extracted from FLYSArtifact.
+	
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  (getFlysContext): Moved to FLYSUtils, updated callers.
+	
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Update callers to getFlysContext.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/MapGenerator.java: Bugfix:
+	  Catch IOException - flys-artifacts compiles again.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Defined "floodmap.barriers" as valid facet
+	  for floodmaps.
+
+	* doc/conf/mapserver/shapefile_layer.vm: New. Currently a copy of
+	  layer.vm. This template will evolve to a special mapserver layer
+	  template with a shapefile data source.
+
+	* src/main/java/de/intevation/flys/wsplgen/FacetCreator.java: Fixed broken
+	  facet name of barriers.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Added
+	  public methods for creating wsplgen and barriers layer files for
+	  mapserver.
+
+	* src/main/java/de/intevation/flys/exports/MapGenerator.java: Use
+	  MapfileGenerator to create new layer files for wsplgen and barriers.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: Added new
+	  method gerRiverBoundary() which returns an Envelope object (which
+	  represents the bounding box of a Geometry) of a riveraxis specified by its
+	  rivername.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: Use
+	  Geometry.getRiverBoundary() to determine the max extent of a river.
+
+	* src/main/java/de/intevation/flys/wsplgen/FacetCreator.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: Use
+	  JTS Envelope to save the bounding boxes of WMS layers.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java:
+	  Adapted the code to apply the changes in GeometryUtils (use Envelope to
+	  determine the max extent of the river axis).
+
+	* src/main/java/de/intevation/flys/exports/MapGenerator.java: New (work in
+	  progress). This Generator will currently return a map configuration in XML
+	  which consists of parameters required by OpenLayers to create a map.
+
+	* doc/conf/conf.xml: Registered the new MapGenerator.
+
+2011-10-27	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml: Do not recommend historical data to load
+	  when having computational discharge curves.
+
+2011-10-27	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Reduce noise, given "error" message was more of "debug" nature.
+
+2011-10-27	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Restore mapping of state id to facets (essentially revert, revision
+	3083 and 3088).
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  (getInitialFacetActivity): Be more explicit on which facets to
+	  introduce inactivated.
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Minor refactoring, declare a string final static.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Restore association from state id to facets.
+
+2011-10-26	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java,
+	  src/main/java/de/intevation/flys/collections/OutputParser.java:
+	  Cosmetics, documentation.
+
+2011-10-26	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Prepare rendering of "other/static wkms" (functional) and
+	  w-differences (not yet fully functional).
+
+2011-10-26  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ChartGenerator.java,
+	  src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Cosmetics.
+
+2011-10-26  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java:
+	  Do not store facets in a map from stateId to list of facets, but in
+	  a pure list instead.
+
+2011-10-26  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Adjusted to newer semantics,
+	  minor cleanups.
+
+2011-10-26  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Resolve association of facets to states in artifacts. This eases
+	merging of outputs and facets and inhibition of unwanted outputs
+	substiantially (at the price of slightly more expensive merging).
+	Also, the semantics of artifacts configuration files (e.g. winfo.xml)
+	is changed (facet elements within an output elements are used for
+	merging).
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Do not store facets in a map from stateId to list of facets, but in
+	  a pure list instead.
+
+2011-10-26  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml: Added dc configuration for some data
+	  that can be loaded from longitudinal section diagrams.
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Cosmetics, docs.
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Let Qs in Longitudinal Diagram be inactive, initally.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  (getInitialFacetActivity): Do not let Facets ending with a 'q'
+	  enter in active state.
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml: Fix broken datacage config.
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml: Configured to include correct id to
+	  clone artifact that produces w-diff.
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Revert correct behaviour of Annotations (the small axis tick shall
+	  always be drawn). Minor refactoring.
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml: Added configuration to include differences
+	  in datacage when longitudinal sections are shown (yet not
+	  functional).
+
+2011-10-25  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml: Cosmetics.
+
+2011-10-24  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* doc/conf/themes.xml: Added theme for w_differences facets.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java
+	  (createSecondAxisRange): Survive parameter-nullness for now.
+	  Added documentation.
+
+2011-10-21	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/themes/ThemeAccess.java:
+	  New. Caching wrapper around an XML document theme. It uses ThemeUtil
+	  to access the values and stores them in instance variable.
+	  Background: ThemeUtil use XPath a lot which is expensive.
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java,
+	  src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Use ThemeAccess to style the annotations.
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java:
+	  Removed some XPath strings. They are in ThemeUtil.
+
+2011-10-21  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java:
+	  Added methods to parse further attributes.
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java:
+	  Apply the theme attributes and use ThemeUtils to get the attribute values.
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Removed the spamy debug output.
+
+2011-10-21	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	To obtain the size of a diagram it is rendered twice. The
+	second time the generated image is omitted so the concrete
+	rendered image is not needed. To save CPU cycles in this pass 
+	the image is rendered to to /dev/null Graphics2D object.
+
+	* src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java:
+	  Added boolean system property "info.rendering.nop.graphics" (default: false).
+	  With this property set the info rendering is done via a new
+	  NOPGraphics2D opbject which does not render the image.
+
+	* src/main/java/de/intevation/flys/java2d/NOPGraphics2D.java:
+	  New. Implements java.awt.Graphics2D trivial empty methods.
+	  This prevents rendering.
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Commented out spamy debug output
+
+2011-10-21  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Draw the text background and use orientation attribute.
+
+2011-10-21  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* doc/conf/themes.xml:
+	  Renamed 'textbackground' to 'backgroundcolor' to have 'color' in the
+	  attribute name.
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java:
+	  Process text attributes correctly.
+
+2011-10-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/CrossSectionKMService.java:
+	  New. Service to lookup the Nth nearest neighbors for a set of given
+	  cross section ids and kms.
+	
+	* doc/conf/conf.xml: Registered service.
+
+	* doc/conf/cache.xml: Cache config.
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  Removed superfluous imports.
+
+2011-10-20  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java:
+	  Added methods to extract further attributes from theme.
+
+2011-10-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/themes.xml: Added main value themes for longitudinal section
+	  charts.
+
+	* src/main/java/de/intevation/flys/themes/ThemeMapping.java: New. This
+	  class stores the name of a facet, the related theme and a pattern
+	  string.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Read the pattern string and store a list of ThemeMapping objects in the
+	  FLYSContext.
+
+	* src/main/java/de/intevation/flys/themes/ThemeFactory.java: Modified
+	  getTheme() which now takes the FLYSContext, the name of a facet and an
+	  optional pattern string. Now, we can have specialized Themes for each
+	  chart type. E.g. the facet "longitudinal_section.w" maps the default
+	  Theme for W lines in longitudinal section charts. If the optional
+	  pattern string matches the pattern ".*(HQ1000)(\D.*)*", the ThemeFactory
+	  will return the Theme "LongitudinalSectionW_HQ1000".
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Use the description of a facet as pattern string to get the relevant
+	  Theme from ThemeFactory.
+
+2011-10-20  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* doc/conf/themes.xml:
+	  Added new theme attributes.
+
+2011-10-19  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix WDifference plots where masterartifact has no range set.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  (addSubtitles): Overridden, the master artifact has no
+	  range.
+
+2011-10-19  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix masterartifact in collections in cases where the original
+	masterartifacts facets do not come first in certain list. Query
+	'backend'/db instead.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Set 'real' master artifact, defined to be oldest belonging to this
+	  collection.
+
+2011-10-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  Modified the method signature of createStaticData() which now also
+	  requires a FLYSArtifact.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  Override createStaticData() to create titles for Qs manually - we want
+	  to display the named main values if existing for the selected Qs.
+
+2011-10-19  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* doc/conf/themes.xml:
+	  ComputedDischargeCurveW and ComputedDischargeCurveQ inherit attributes from
+	  theme 'Text'.
+
+2011-10-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Fix for flys/issue316
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java: Added
+	  constructor to not sort the data.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Use the not sorting constructor of StyledXYSeries.
+
+2011-10-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  moved the code to create WSP W and Q facet names to FLYSUtils.
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: New functions for
+	  creating WSP W and Q facet names and for querying a named main value
+	  based on a given gauge and value. The names of W and Q facets will now
+	  depend on the selected Q and Q mode: if the mode is "q at gauge" and a
+	  named value is found for the given value, the facet's name contains the
+	  named value instead of the value itself.
+
+2011-10-19  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix datacage configuration to let client load ZUS and flood
+	protections.
+
+	* doc/conf/meta-data.xml: Use 'ids' instead of 'id' to help client.
+
+2011-10-19  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java:
+	  Added methods to parse text attributes from theme document.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java,
+	  src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Apply a theme to axis annotations.
+
+2011-10-19	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Create (data) Label from data string (client will be adjusted to send
+	the name).
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  Create label from input data string, documentation added, junk
+	  removed.
+
+2011-10-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java(zoom):
+	  Do not crash if no axis is given.
+
+2011-10-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added
+	  getFormat() which extracts the format string from XML request document.
+
+	* src/main/java/de/intevation/flys/exports/ChartExportHelper.java: Adapted
+	  method signatures of exportImage(), exportSVG() and exportPDF(). All
+	  methods now take a CallContext object which stores extra chart export
+	  parameters.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Enabled PDF and SVG chart exports based on the "format" string given in
+	  the XML request document.
+
+2011-10-18  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Simplify rendereing W(Q)Kms in WDifferencesCurveGenerator.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  (doWOut, doWOut): Simplified.
+
+2011-10-18  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Render zus and flood-protections in WDifferences-diagrams.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Added basic respect of "other.wkms"- facets.
+
+
+2011-10-18  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Adjusted Datacage-Configuration to fetch "extra longitudinal...."
+	(.zus) - waterlevels in certain case (in system-part).
+
+	* doc/conf/meta-data.xml: Adjusted to present extra-kms with
+	  staticwkms factory in certain case.
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* doc/conf/themes.xml:
+	  Added new virtual theme for text with the attributes 'font', 'textcolor'
+	  and 'textsize'.
+
+2011-10-18  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Give StaticWKmsArtifacts proper names, and pre-deselect them.
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java:
+	  (getInitialFacetActivity): Overridden to let facets enter plot
+	  inactively.
+	  (setup): Give Facets the name of the Wst.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java:
+	  Refactored to expose getWKmsName separately.
+
+2011-10-18  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Adjusted Datacage-Configuration to fetch flood-protections when
+	waterlevels are requested (in system-part).
+
+	* doc/conf/meta-data.xml: Adjusted to present flood-protections with
+	  staticwkms factory in certain case.
+
+2011-10-18  Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Register staticwkms factory.
+
+	* doc/conf/conf.xml: Register staticwkms factory to spawn StaticWKms-
+	  Artifacts.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fetch name of static WKms.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java:
+	  Removed dependence on "kind", but fetch name for created WKms.
+	
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java:
+	  Remove dependence on Kind.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Store parameterization in data, not in Artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java:
+	  Resolve col_pos and wst_id field, use data instead.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Add convenience-method to add defaultdata (string).
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  (addStringData): Add Default (String) Data .
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Add a new Artifact and Facet (StaticWKmsArtifact, WKmsFacet) to
+	access WKms obtainable with the WKmsFactory.
+
+	* src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java:
+	  New, artifact with single state to get WKms from WKmsFactory.
+	
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java:
+	  New Facet to display W over km.
+	
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added new type name.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Add WKMsFactory to access 'static' wst-data.
+
+	* src/main/java/de/intevation/flys/artifacts/model/StaticWKmsCacheKey.java:
+	  Cache Key for the static WKms data.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java:
+	  New, creates WKms from wst-id and column. Does not yet fetch the
+	  name.
+
+	* doc/conf/cache.xml: Added cache configuration for static wkms data.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java:
+	  Cosmetics, docs.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java:
+	  Added constructor that takes name, docs.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix issue that Annotations do not come with theme/style.
+	
+	* doc/conf/themes.xml: Added "Annotations" default style.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Set style of annotations, minor cosmetics.
+
+2011-10-17	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Made inner class ThemeList static.
+	  s/new Integer(small)/Integer.valueOf(small)/
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  The inner class ThemeList makes now use of ManagedDomFacet to read the
+	  attributes of Facets saved in the Collection's attribute.
+
+2011-10-17	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue314: Variables in datacage are now case insensitive.
+	(H2 returns meta data variables uppercase)
+
+	* doc/conf/meta-data.xml:
+	  Made a statement more precise. Added some debug output.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java:
+	  Use uppercase variable names.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java:
+	  Variables are now treated as uppercase.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  Input variables are now treated uppercase.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java:
+	  Prepared Facets to support a "visible" attribute.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java: Adapted
+	  the constructor call of ManagedFacetAdapter.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  The inner class ThemeList now supports the "visible" attribute of
+	  ManagedFacets.
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java:
+	  Added method to apply line type.
+
+	* doc/conf/themes.xml:
+	  Changed initial default value for line type.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue226 (W-INFO: Dauerlinienberechung /Abbbildung x-Achse)
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java:
+	  Set the upper bound of these charts to 364.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java:
+	  Added a function that returns a Gauge based on its name.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java:
+	  Create proper descriptions for facets.
+
+	* src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: Added a
+	  setter for labels.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Use the string returned by Facet.getDescription() as series names. The
+	  ThemePanel and the Legend will always display the same titles for curves
+	  now.
+
+
+2011-10-17	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix flys/issue363 (W-INFO/ Abflusskurve, Extremwert-Rendering).
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java:
+	  (parseLineWidth): New. Get line width from Document.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  When adding annotations, parse line width from theme, set it.
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  When painting, set Paint and stroke early enough.
+
+2011-10-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Put the
+	  config directory into the Velocity context. It is available as
+	  '$CONFIGDIR' in templates.
+
+	* doc/conf/mapserver/mapfile.vm: Set the debug file to
+	  "$CONFIGDIR/flys-user-wms.log" and added a LEGEND section.
+
+	* doc/conf/mapserver/wsplgen_class.vm: Adapted the class names. Those
+	  names are displayed in the image served by GetLegendGraphic.
+
+2011-10-14	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Add possibility of programmatic configuration of initial "activity"
+	state (active or inactive) of (Managed)Facets by introducing
+	FLYSArtifact.getInitialFacetActivity. This method shall be overriden
+	by subclasses where Facets are wanted to come to live inactive.
+	Artifacts will be asked only once how the MangedFacet should come to live,
+	namely when AttributeWriter finds a genuinely new Facet.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java
+	  (getInitialFacetActivity):
+	  New function to let Artifact decide whether a ManagedFacet shall
+	  initially be set to active or inactive.
+
+	 * src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	   Accept database in constructor. For genuinely new Facets, spawn its
+	   mother artifact and ask whether the (Managed)Facet shall be active
+	   or inactive (initially).
+
+	 * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java
+	   (buildOutAttributes): Pass database to AttributeWrite (which needs it
+	   to spawn artifacts), rename items parameter to reflect content.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Cosmetics, removed obsolete imports.
+
+	* src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Removed obsolete imports.
+
+2011-10-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Added a further attribute "mark.selected" to the barriers feature type.
+	  This attribute is used in the client, where we are not able to remove it
+	  properly. Reading the GeoJSON string without this attribute is no longer
+	  possible... strange!
+
+2011-10-12	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added i18n for label of wdiff "pair select" states data.
+
+	* 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 for
+	  state.winfo.waterlevel_pair_select .
+
+2011-10-12	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Addressed "Wrong labels" [6] part of flys/issue371 (W-INFO / Differenzen:
+	Anmerkungen zur Umsetzung) .
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesExporter.java:
+	  Adjusted variable names to avoid conflict in subclasses, adjusted
+	  default value for i18n string.
+
+2011-10-12	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Cosmetics: Space after full stop in commments, slightly improved
+	  documentation, added one debug message.
+
+2011-10-12	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix remainder of flys/issue304 (Erweiterte Funktionen W-Differenzen) .
+
+	* doc/conf/artifacts/winfo.xml: Removed obsolete data of
+	  WDifferencesState.
+
+	* src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java:
+	  Re-enable facet-filtering, but adjust filters before that happens
+	  (former longitudinal_section output is now w_differences output).
+
+2011-10-11	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added wdiff-chart translations.
+
+	* 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 for wdiff.
+
+2011-10-11	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Expose translateable Strings as constants.
+
+2011-10-11	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix most labels in w-differences charts.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Remove most static final i18n-variables in favor of direct String
+	  usage or usage of methods. By this, allow easier adoption of labels
+	  in subclasses.
+
+2011-10-11  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue383 (Zweite Y-Achse wird beim Zoomen/Verschieben nicht angepasst.)
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Call adjustAxes() before applying zoom settings with autoZoom(). We need
+	  to add new y-axes first before we adjust their ranges.
+
+2011-10-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  s/new Integer(small)/Integer.valueOf(small)/
+
+2011-10-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Do not fire change events for each and every data point added.
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix WDifferences with more than one Pair (crashed due to incorrect index for
+	facets.)
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  Fix wrong index for difference facets (allows for more than one
+	  difference facet per artifact without crashes).
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Use slightly modified doWOut-implementation from
+	  LongitudinalSectionGenerator (here need to add Ws to different axis).
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  Fix import/reference.
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Let WaterlevelPairSelectState include diffids-data (if any) to enable
+	future work on repopulation of Grid in GUI when jumping back
+	(reparameterization).
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  (createItems): Override to include old data.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue150 (Diagramm: Anzeige von W bergauf)
+	flys/issue345 (W-INFO / Wasserspiegellagenberechnung, Diagrammausgabe)
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Corrected the determination to invert the x axis, so that waterlines
+	  will start with their highest value at the left and end with their
+	  lowest values at the right.
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java:
+	  Cosmetics, added an (@Override) annotation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Cosmetics, added documentation.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  Cosmetics, whitespace after full stops in comments, added
+	  (@Override) annotation.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue220 (Diagramm: Achsenbeschriftungen an verschiedenen Achsen müssen gleich aussehen)
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Adjusted the label font of the 2nd y-axis - now, both axes labels look
+	  equal.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue189 (WINFO/Dauerlinie: Sortierung der Berechnungsausgabe nach Dauerzahlen aufsteigend)
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQDay.java: Added a
+	  method that determines if the items (days) in this object are increasing
+	  or not.
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveExporter.java:
+	  Changed the order of the CSV export - the highest day is at the top of
+	  the export; the lowest day is at the bottom.
+
+2011-10-07	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Worked on flys/issue150 (Diagramm: Anzeige von W bergauf).
+	Still does not work in all cases.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java (generateChart()):
+	  Reordered calls to ensure that the inversion of the x axis is
+	  not eliminated by other chart generation steps as a side effect.
+
+	 * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	   Assuming that JFreeChart is inverting axis automatically if the
+	   KMs are reversed ordered only do invert only in some situations.
+
+	   Do not invert axis for Q (@Ingo: This is wrong! We must do this
+	   if we are only displaying the Qs and the Ws are deactived).
+
+2011-10-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Take care on empty ranges while preparing ranges for single points.
+
+2011-10-07  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue114 (W-INFO: Wasserspiegellagenberechnung / Ort (Spezialfall: Generierung eines Diagramms bei punkthafter Berechnung))
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Expand
+	  ranges for x and y axes if there is just a single point in a series -
+	  JFreeChart requires a range where lower <> upper.
+
+2011-10-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RangeState.java,
+	  src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Add whitespace after full stop in comments, minor doc improvements.
+
+2011-10-07  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue353 (W-INFO / Wasserspiegellagenberechnung, Diagramm)
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adapted the process of collecting outs for Artifacts/Facets. We will now
+	  call OutGenerator.doOut() for each Artifact and Facet - never mind if
+	  the facet is activated (visible) or not. The OutGenerator should decide
+	  on its own whtat to do with facets which are "marked" as _not_ visible.
+
+	* src/main/java/de/intevation/flys/exports/OutGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ChartGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ReportGenerator.java,
+	  src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/AbstractExporter.java,
+	  src/main/java/de/intevation/flys/exports/ATExporter.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/WDifferencesCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java:
+	  Adapted the signature of OutGenerator.doOut(). There will be a new
+	  boolean parameter "visible" that determines if the facet specified in
+	  this method is visible for this output or not.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Now,
+	  that we walk over every Artifact/Facet, we are able to collect min/max
+	  data for all axes. We store these information and use them to set the
+	  ranges of x and y axes. The result of this: a chart can have proper axes
+	  set without any data in it.
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: Use
+	  min/max ranges stored while calling doOut() for each Artifact/Facet
+	  instead of fetching those information from chart's Datasets (which could
+	  be null).
+
+2011-10-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/Formatter.java: Changed the max
+	  number of digits for AT exports from 0 to 2.
+
+2011-10-06	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java: Get rid
+	  of buggy first line code.
+
+2011-10-05	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	fixed flys/issue201
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Replace >= with > in km index lookup because last km was not found.
+
+2011-10-05	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	fixed flys/issue177
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Revert rev2245. Code works fine now! :-)
+
+2011-10-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: Stores a
+	  list of XYTextAnnotations instead of FLYS specific Annotations. This
+	  makes this class suitable for other annotation types as well.
+
+	* src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java:
+	  Both facets' getData() will now return an instance of FLYSAnnotation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java:
+	  Create XYTextAnnotations used to instantiate an object of
+	  FLYSAnnotation.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Removed the code to add annotations to the plot. This task is general
+	  enough to move this code to parent class.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Instances of this class are now able to store - besides first and second
+	  axes datasets - a list of annotations. This annotations are added to the
+	  plot after the datasets have been added. To support LegendItems for
+	  those annotions, it was necessary to create a the LegendItemCollection
+	  by ourself. This work is done while applying the themes for each series
+	  in the chart.
+
+2011-10-05	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Removed code duplication of guessWaterIncreasing()
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKms.java(allKms, allWs):
+	  Added methods to fetch all kms and all ws.
+
+	* src/main/java/de/intevation/flys/utils/DataUtil.java: Generalized to 
+	  get WKms as arguments.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WQ.java: Implements
+	  the extended WKms interface.
+
+	 * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	   Uses the generalized DataUtil.guessWaterIncreasing().
+
+2011-10-05  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue347 (W-INFO / Wasserspiegellagenberechnung, Längsschnittdiagramm)
+	flys/issue303 (Keine Streckenfavoriten, wenn nur Q im Längsschnittdiagram ausgewählt)
+	flys/issue353 (W-INFO / Wasserspiegellagenberechnung, Diagramm)
+
+	* src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: New. A
+	  wrapper for Annotations which allows us to provide a description for a
+	  set of annotations.
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java:
+	  The getData() will now return an instance of FLYSAnnotation that wraps
+	  the Annotations returned by the AnnotationArtifact. The lebel of
+	  FLYSAnnotation is the description of this Facet.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Modified the way to add Annotations. We will no longer create an empty
+	  series to support a LegendItem for a set of Annotations, but we will add
+	  a LegendItem manually to the LegendItemCollection of the plot. In
+	  addition, we are now able to display annotations if one of the two
+	  y-axes are missing. If there are no y-axes existing, we are not able to
+	  display annotations yet.
+
+2011-10-05	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Lifted the wrong point. Now all backjump corrections look fine. :-)
+
+2011-10-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Fixed bugs and make use of a cache for annotations now.
+
+2011-10-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Worked on flys/issue31
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Simplified the code a lot. Needs testing. Maybe flys/issue31 is gone
+
+2011-10-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Fetch the WstUnit value from river - the Wst itself no longer supports a
+	  Unit iself.
+
+2011-10-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java,
+	  src/main/java/de/intevation/flys/utils/DoubleUtil.java: Moved some generic
+	  double array code to DoubleUtil.
+
+2011-10-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/DoubleUtil.java (interpolateSorted):
+	  Added code to linear interpolate double values in a sorted array.
+	  Keys and values are given as double arrays. Keys need to be sorted.
+
+2011-10-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  Removed superfluous imports.
+
+2011-10-04  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue330 (Dauerlinie kann nicht berechnet werden)
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation3.java:
+	  Add a problem if no data was found for duration curves.
+
+2011-10-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/themes/Theme.java,
+	  src/main/java/de/intevation/flys/themes/DefaultTheme.java: Added
+	  getter/setter methods to provide a facet (string) and index (int). Both
+	  values are written as attribute to the Theme's XML representation.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Set the "facet" and "index" values of themes.
+
+2011-10-04  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue346 (W-INFO / Anzeige der Höheninformation)
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Modified x and y axis title. The x axis title is now "RIVERNAME-km"; the
+	  y axis title is now "W[WST_UNIT]" where WST_UNIT depends on the unit of
+	  the WST.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Modified i18n expressions for
+	  x and y axis of longitudinal section charts.
+
+2011-09-30  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue351 (W-INFO / Wasserspiegellagenberechnungen)
+
+	* src/main/java/de/intevation/flys/utils/DoubleUtil.java: Modified the
+	  explode() function that returns a list of values specified by min, max
+	  and an interval. If the last value, determined by the interval, is
+	  bigger than the max value, it is not included in the result list.
+
+2011-09-30  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	flys/issue334 (Querprofil-Diagramm: Ausgabe dieses Diagrammtyps
+	möglich, obwohl WSP Berechnung keine Ergebnisse liefert)
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  (compute): Add Facets regarding CrossSections only if data available.
+
+2011-09-29  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue176 (Diagramm: Benennung eines Abflusses bei gewählter Höhe am Pegel)
+	flys/issue349 (W-INFO / Wasserspiegellagenberechnung, Längsschnittdiagramm)
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Create titles for W and Q waterlevel facets with proper fractions.
+
+2011-09-30  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  (createStaticData, getLabels): Create proper labels for differences.
+
+2011-09-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue244 (WINFO: Export von AT-Dateien im ersten Teil unterschiedlich)
+	flys/issue332 (W-INFO / Berechnung Abflusskurve, Export, FLYS 2.5)
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java: Due to a rounding
+	  issue the w's of the first line underun the minimal w of the curve at times.
+	  An extra test was introduced to suppress the output of the q's of the wrong w's.
+
+2011-09-28  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	Use StringUtil.wWrap , fix wrong loop, minor refac and cosmetics.
+
+2011-09-28  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java:
+	Extracted/use StringUtil.wWrap .
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java:
+	  Log the gauge which has been determined.
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue332 (W-INFO / Berechnung Abflusskurve, Export, FLYS 2.5)
+
+	* src/main/java/de/intevation/flys/exports/ATExporter.java: Store the
+	  master Artifact which is set via setMasterArtifact(). This is required
+	  for meta information used while preparing the header row of AT exports.
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java: Print a header
+	  row into the AT export for being compatible with desktop FLYS.
+
+	* 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 header row for AT
+	  export files.
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue328 (W-INFO / ÜSK: Auswahl der Wasserspiegellage / Auswahlunterstützung)
+
+	* doc/conf/meta-data.xml: Added an out 'waterlevels' that might be used to
+	  fetch user specific waterlevels (same as longitudinal sections, but
+	  without Q facet).
+	  In addition, the system specific datacage stuff is now fetched, when:
+	  a) no user-id is given
+	  b) a user-id is given and there is a parameter 'load-system'
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java:
+	  If the label of the WQKms object specified by the waterlevel selection
+	  begins with a "Q", the label is wrapped into a "W()", e.g. "W(Q=1200)".
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Made
+	  feed() able to remove existing data items from Artifact's data pool.
+	  Therefore, the value for the item which should be removed needs to an
+	  empty string.
+
+2011-09-28  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Extracted StringUtil.unbracket from WaterlevelSelectState.strip.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java:
+	  Extract and use StringUtil.unbracket, minor doc.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Changed access of getCurrentState() from protected to public.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: We
+	  use the configured Outputs instead of the actuel Outputs of an Artifact
+	  to make recommendations, now. This has the bad side effect of giving
+	  recommendations for Outputs that we might _NOT_ be able to produce. But
+	  otherwise, we would not be able to give recommendations for states with
+	  long calculation times that start background threads for calculation (as
+	  WSPLGEN caluclations does).
+
+2011-09-27  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Enable "auto-scaling" axis for waterlevels in WDifference-plots.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  (createSecondaryAxis, zoomY): Refactored to allow modification in
+	  siblings.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  (createSecondaryAxisRange): Override to achieve expected behavior.
+	  Also adjusted label.
+
+2011-09-27  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	In W-Difference Calculation, respect indices of selected facets.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  (computeAdvance): Respect index of selected facets.
+
+2011-09-27	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue317: (Querprofil-Diagramm: Referenzen auf CrossSectionApp entfernen)
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Removed dependency to demo app.
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  Refactored to use logic from the models.
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Lines.java:
+	  Moved some logic from the demo app to this model.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/wsplgen/FacetCreator.java: New. Code
+	  from FloodMapState moved to its own class with the intent, to use it in
+	  classes different from FloodMapState.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Removed the inner class FacetCreator.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Stores
+	  an instance of FacetCreator.
+	  NOTE: Maybe we should move the WSPLGEN parameters into an own class
+	  which might be serializable.
+
+	* src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Use the
+	  FacetCreator instance stored in the WSPLGENJob to create a new WSPLGEN
+	  facet if the calculation was successfully (without errors). Finally, the
+	  facets of FacetCreator are added to the Facet list of the FLYSArtifacts.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Put Artifact in background mode first before starting WSPLGEN, otherwise
+	  a very fast errors (call CallContext.afterBackground() before Artifact is
+	  in Background mode) might lead to an inconsistent state.
+
+	* src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: Repaired
+	  broken error num parsing.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: Track
+	  critical errors as well (improved regular expression for errors).
+
+2011-09-27	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* ChangeLog: Fixed whitespace usage.
+
+2011-09-27	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/RiverService.java,
+	  src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java
+	  src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Removed superfluous imports.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue68 (Diagramm: Werte an der Y-Achse benötigen i18n)
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added a
+	  method to retrieve the current/preferred locale specified by CallMeta.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Introduced two methods localizeDomainAxis() and localizeRangeAxis().
+	  Both methods of this class override the NumberFormat used to format axes
+	  numbers. Those methods are called by localizeAxes() - which has private
+	  access - for each domain and range axis of the current XYPlot.
+
+2011-09-27	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* doc/conf/cache.xml: Number of cached annotations was much
+	  to low.
+
+2011-09-27  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Improved w-differences diagram generation where multiple differences
+	can be shown.
+
+	* src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java:
+	  New facet type.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  Employ new DifferenceCurveFacet, return CalculationResult that can
+	  store more than one WKms.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue320 (ÃœSK:Mapserver hat Probleme beim Shapefilepath mit "../" im Pfad)
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Use
+	  File.getCanonicalPath() to substitute "../" in shapefile directories.
+
+2011-09-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  The extent of the WSPLGEN result layer is now specified by the extent of
+	  the CrossSectionTracks that matches the start and end kilometer of the
+	  WSPLGEN calculation.
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: New function
+	  that creates the OpenLayers bounding box based on two Geometries.
+
+2011-09-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Improved w-differences diagram generation with included "absolute"
+	values.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Fixed one (of two) incorrect labels.
+	  (doWaterlevelOut): Survive non-found gauge, fetch kilometer and w
+	  instead of values for w and q.
+
+2011-09-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Add positional-conflict-awareness when merging Facets for a
+	FLYSArtifactCollection. First come first serve.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	  (writeFacets): First, sort incoming facets into 2 groups:
+	  "genuinely new" and "already there", then for each new check
+	  whether the position is already taken. If so, push "up" (position++)
+	  until no conflict exists anymore.
+
+2011-09-26  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue296 (Karte: Bezeichnungen verbessern)
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  I18N of the WSPLGEN and barriers facets (WMS layers).
+
+	* 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 strings for the wsplgen
+	  and barriers WMS layers.
+
+2011-09-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix (revert) access to position in ManagedDomFacet.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java:
+	  (getPosition): Do not add prefix when querying position ("pos")
+	  attribute, add a logger for faster future debugging.
+
+2011-09-23  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Squash positional conflict-bug.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	  (mergeFacets): Removed, replaced in parts by pickFacet.
+	  (pickFacet): New, return facet to be added to document.
+	  Documentation added.
+
+2011-09-23  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Squash a bug about wrongly named "art:pos" attribute in ManagedDomFacet (was
+	"pos"). Added documentation from commit message.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java:
+	  Added documentation (commit message with minor adjustments).
+	  (getPosition, setPosition): Include PREFIX in attribute name.
+
+2011-09-23  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Refactoring, doc.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	  (mergeFacets): Removed, replaced in parts by pickFacet.
+	  (pickFacet): New, return facet to be added to document.
+	  Documentation added.
+
+2011-09-23  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	  Documentation added.
+
+2011-09-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Subclasses the FLYSService now - this should improve the database
+	  connection handling.
+
+2011-09-22  Bjoern Schilberg <bjoern.schilberg@intevation.de>
+
+	* doc/mapserver/mosel-mapfile.map:
+	  Full blown mosel wms mapfile.
+
+2011-09-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/FLYSService.java:
+	  New. A subclass of DefaultService which is used in FLYS to init and
+	  shutdown database connections.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java,
+	  src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java,
+	  src/main/java/de/intevation/flys/artifacts/services/RiverService.java:
+	  Centralized the initialization and shutdown of database connections.
+	  These services now subclass FLYSService which handles the database stuff.
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: The
+	  current database connection is not closed here - this is done in a
+	  Service or in the CallContext.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: It's
+	  not necessary to create new database connections here. We already have
+	  an existing connection which is initialized by CallContext.
+
+2011-09-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java:
+	  Override validate() of parent classes to suppress "step" validation
+	  which is not present in this state.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RangeState.java: Added
+	  new method validateBounds() which really just validates a boundary
+	  without "step" parameter.
+
+2011-09-21  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added WaterlevelArtifact to accompany WINFOArtifacts in WDifferencesPlots.
+
+	* src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java:
+	  New WaterlevelArtifact.
+
+	* doc/conf/conf.xml:
+	  Added configuration for WaterlevelArtifact configuration (path to state-xml)
+	  waterlevel-factory.
+
+	* doc/conf/artifacts/waterlevel.xml:
+	  New, trivial state description for Waterlevelartifact.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java:
+	  New, only state for WaterlevelArtifact.
+
+2011-09-21  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java,
+	  src/main/java/de/intevation/flys/collections/AttributeWriter.java,
+	  src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Cosmetics, documentation.
+
+2011-09-21  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added WaterlevelOut-processing ability to WDifferencesCurveGenerator.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Process LONGITUDINAL_W facets.
+
+2011-09-21  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Improved WDifferenceState in preparation to be able to deal with multiple
+	pairs for differences.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  Prepare multiple-pairs-case.
+
+2011-09-21  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue315 (Überschwemmungsfläche: String bei Streckenauswahl)
+
+	* doc/conf/artifacts/winfo.xml: Added a new state for floodmap's range
+	  input. This state will accept a km range only, there is no step width.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java:
+	  New. The state which is used to enter a km range with step width.
+
+2011-09-21  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics, improved debug output, doc.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Improved debug output.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java:
+	  Added documentation.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java,
+	  src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Removed commented code.
+
+	* src/main/java/de/intevation/flys/collections/AttributeParser.java:
+	  Whitespace cosmetics.
+
+2011-09-21  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Avoid NullPointerException when drawing XYChart without data.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Guard calls to dataset to avoid NullPointerException.
+
+2011-09-21  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue325 (FLYS Client: Auswahl des DGM zeigt numerischen Wert an)
+
+	* src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java: Write
+	  a better label for the selected DEM into the static DESCRIBE. Use the
+	  name of the DEM file as label instead of the database id.
+
+2011-09-16  Bjoern Schilberg <bjoern.schilberg@intevation.de>
+
+	* doc/mapserver/fontset.txt:
+	  Added initial font set for km_annotation layer.
+	* doc/mapserver/symbols/symbols.sym:
+	  Added square symbol for km and fixpoint layer.
+	* doc/mapserver/saar-mapfile.map:
+	  Adjust styling in the flys karte-archiv way.
+
+2011-09-19  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.5
+
+	* Changes: Prepared changes for release.
+
+2011-09-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Removed
+	  needless imports.
+
+2011-09-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data.xml: Improved datacage configuration for DEMs.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Query DGMs by the given ID in the FLYS data pool - not by given range
+	  values.
+
+2011-09-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data.xml: Added a section for DEMs to the floodmap
+	  section.
+
+2011-09-16  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix build.
+
+	* src/main/java/de/intevation/flys/artifacts/state/WDifferencesState.java:
+	  Fix build.
+
+2011-09-16  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Take a given WaterlevelPair-String, load artifact and plot the diff.
+
+	* src/main/java/de/intevation/flys/artifacts/state/WDifferencesState.java:
+	  Load correct artifacts.
+
+2011-09-16  Bjoern Schilberg <bjoern.schilberg@intevation.de>
+
+	* doc/mapserver/*: Added inital mapserver configuration files.
+
+2011-09-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data.xml: Moved the system specific configuration into a
+	  macro to the top of the configuration document. Call this macro at the
+	  end of the user specific part and in the part that should contain the
+	  system specific stuff only!
+
+2011-09-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Defined createItem() here and adapted some method signatures, because I
+	  need the CallContext deeper in code than expected.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java:
+	  Write the name of the selected waterlevel into the static DESCRIBE.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java:
+	  Adapted method signatures that have been changed in DefaultState.
+
+	* src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Removed method createItem() which is now defined in the upper class
+	  DefaultState.
+
+	* 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 string for selected
+	  waterlevel that is displayed in static UI.
+
+2011-09-16  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 state label for
+	  waterlevel selection.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java:
+	  Validate incoming data string and strip brackets.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Fetch waterlevel data from external Artifact if existing. If no external
+	  Artifact is specified that provides waterlevel data, we gonna try to
+	  fetch it from the current Artifact.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  Removed needless import which caused compile errors.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Added a transform() method. This method should be used to transform
+	  input data in form of a string into a better data structure. This state
+	  provides a simple implementation which just returns a StateData object
+	  that contains exactly the input string.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Transform
+	  input strings using DefaultState.transform() before they are added to
+	  its data pool.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Made collection solid for the case if it has no Artifacts.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: Fetch Artifact
+	  from ArtifactDatabase properly. Write better error logs if that process
+	  fails.
+
+2011-09-14  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Extend WDifferences branch to have calculations in dedicated, new state.
+
+	* doc/conf/artifacts/winfo.xml: Added new State and Transition in
+	  WDifferences-branch.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java:
+	  New state.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  Specify to not take input, prepare getting other facets.
+
+2011-09-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java: This
+	  state now desires the UIProvider 'dem_datacage_panel'.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java:
+	  New. This state is used to define the desired UIProvider
+	  'wsp_datacage_panel'.
+
+	* doc/conf/artifacts/winfo.xml: Added a new way to start a WSPLGEN
+	  calculation when choosing the calculation type 'floodmap'.
+
+2011-09-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java: Merge
+	  facets only if their name AND their owner artifact are equal.
+
+2011-09-14  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Partial Fix flys/issue304 (3) (Baseline).
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Show Baseline in WDifferencesPlot.
+
+2011-09-14  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix flys/issue310 (cross-section theme).
+
+	* doc/conf/themes.xml: CrossSection-Themes: profile thin, water blue.
+
+2011-09-14  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix flys/issue310 (cross-section theme).
+
+	* doc/conf/themes.xml: CrossSection-Themes: profile thin, water blue.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Add an quick and simple error to the report if an error occured while
+	  WSPLGENJob creation.
+
+	* 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 error messages for
+	  WSPLGEN job creation errors.
+
+2011-09-13  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix crash when drawing StickyAxisAnnotation when no range was given.
+	User-directed issues remain ( flys/issue303 ).
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java
+	  (draw):
+	  Guard access to domainAxis, rangeAxis and the corresponding ranges.
+	  Warn and exit if any was null.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue290 (Karte: Eingabe von Differenzen zw. WSP und Gelände findet
+	keine Ausprägung in der Karte)
+
+	* doc/conf/mapserver/wsplgen_class.vm: Added styles for DIFF attribute.
+
+	* doc/conf/mapserver/mapfile.vm: Set debug default to '5'.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Add a first WSPLGEN status message (notifies the user about a
+	  queued job) after the job has been added to the Scheduler.
+
+	* 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 english and german
+	  status message text.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: Write
+	  shapefiles only if there are features for it existing.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  A WSPLGEN facet is only created, if the calculation was successfully
+	  added to the Scheduler. A barrier facet is only created if the WSPLGEN
+	  calculation has been added to the scheduler AND if there are digitized
+	  geometries existing.
+
+2011-09-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Copied and slightly modified implementation of guessWaterIncreasing from
+	WQKms to (new) DataUtils. Accidentally commited usage in last commit, to
+	correct orientation of diagram (invert x axis).
+
+	* src/main/java/de/intevation/flys/utils/DataUtils.java:
+	  New file with guessWaterIncreasing implementation from WQKms,
+	  slightly adjusted.
+
+2011-09-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java:
+	  Cosmetics.
+
+2011-09-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added CSV Export for W-Differences.
+
+	* doc/conf/conf.xml: Added Exporter.
+	
+	* doc/conf/artifacts/winfo.xml: Removed transition over distances
+	  state, added export outputmode and facet.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesExporter.java:
+	  New, CSV-Exporter for WDifferences.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  Add export facet.
+
+2011-09-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DataFacet.java:
+	  Cosmetics, docs.
+	
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java,
+	  src/main/java/de/intevation/flys/exports/AbstractExporter.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveExporter.java:
+	  Cosmetics.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Sourced the code to append a concrete data item to the static DESCRIBE
+	  part out to an own method (appendStaticData()).
+
+	* src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java:
+	  Override appendStaticData() to suppress the GeoJSON string to be
+	  included in the static DESCRIBE.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java,
+	  src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Removed needless imports.
+
+2011-09-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/CalculationMessage.java:
+	  New. A subclass of Message (in the Artifacts system). It stores a
+	  string message and a progress (in form of step x/y).
+
+	* src/main/java/de/intevation/flys/wsplgen/JobObserver.java: The observer
+	  now writes background messages into the artifact system using the
+	  CallContext.addBackgroundMessage(). We use instances of
+	  CalculationMessage here, that makes the WINFOArtifact able to put
+	  progress information into the Artifact's DESCRIBE as well.
+	  
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Write
+	  status message and progress information into the DESCRIBE if the
+	  Artifact has started a background process.
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix flys/issue280 .
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Do not draw boxes around annotations.
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix flys/issue279 .
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Fix behaviour in various thinkable malconditions.
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Made one case of cross section fetching more robust.
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix Facet name and Legend of W-Differences, also insert state to specify
+	distance.
+
+	* doc/conf/artifacts/winfo.xml: Add additional transitions to walk over
+	  distances state.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  Set facets description.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  Take facets description as legend.
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added configuration to kick-in w-differences branch of winfo.
+
+	* doc/conf/conf.xml: Register new (w-differences) OutputGenerators.
+
+	* doc/conf/artifacts/winfo.xml: Register new state and transitions.
+
+	* src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Added new (w-differences) calculation mode.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveInfoGenerator.java:
+	  New file, implementing naive approach to display w-differences
+	  (accidentially omitted in last commit).
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added very stubby WDifferences State/OutGenerator for WINFOArtifact.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added w_differences facet type.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java:
+	  New file, implements naive approach to calculate w-differences (of
+	  calculations identified by hardcoded uuids!) and register respective facet.
+
+	* src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java:
+	  New file, implementing naive approach to display w-differences.
+
+2011-09-09  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Implement getArtifact(uuid,context) in FLYSUtils.
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java (getArtifact):
+	  Added implementation, partially resolving a TODO. Added logger instance.
+
+2011-09-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENCalculation.java:
+	  New. This sublcass of Calculation saves warnings and errors that occur
+	  while WSPLGEN is running.
+	  Note, that the interface of this class doesn't exactly apply the interface
+	  of Calculation. Maybe, we should generalize this interface!
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Stores
+	  an instance of WSPLGENCalculation now. We use this instance to save
+	  warnings and errors.
+
+	* src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: Use the
+	  WSPLGENCalculation to save errors and warnings.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENReportFacet.java:
+	  New. This facet is used for WSPLGEN reports. It stores an instance of
+	  WSPLGENCalculation which saves ERRORS and WARNINGS that occur while
+	  WSPLGEN execution.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Create a WSPLGENReportFacet for WSPLGEN reports.
+
+	* doc/conf/conf.xml: Added an OutputGenerator 'report' for WSPLGEN
+	  reports.
+
+	* doc/conf/artifacts/winfo.xml: Added an output 'report' for WSPLGEN
+	  reports.
+
+2011-09-08  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java,
+	  src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Cosmetics, docs.
+
+2011-09-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/wsplgen/JobObserver.java: Small
+	  adjustments that makes it easier to subclass this observer.
+
+	* src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: New. A
+	  sublcass of JobObserver which analyses the WSPLGEN output for errors and
+	  warnings.
+
+	* src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Use
+	  JobObserver and ProblemObserver to track the whole WSPLGEN output and
+	  print number of errors/warnings to log output.
+
+2011-09-08  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added translation of w_differences.
+
+	* src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_de.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages.properties:
+	  Added "w_differnces" translation.
+
+2011-09-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Appended
+	  a new attribute 'background-processing' to the DESCRIBE of this
+	  Artifact. Its value is 'true' if this Artifact has started a background
+	  thread which has not finished yet - otherwise it is 'false.
+
+2011-09-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java,
+	  src/main/java/de/intevation/flys/utils/GeometryUtils.java: Moved the
+	  code to determine the extent of a river based on its axis to
+	  GeometryUtils.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Determine the extent of the selected river and set the WMSLayerFacet's
+	  extent attribute.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added Themeing support for CrossSection Diagrams.
+
+	* doc/conf/themes.xml:
+	  Added new themes and mappings, slightly modified longitudinalsection
+	  theme.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	In CrossSection Diagram fix subtitle to display the km of which the data is
+	actually displayed (maybe contrasting users wish).
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  (getCrossSectionSnapKm): New method to fetch the actual km of crosssection.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Add correct km to charts subtitle.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix various display-issues like i18n in cross-section diagram.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Set description of facets to expected values.
+
+	* src/main/java/de/intevation/flys/artifacts/exports/CrossSectionGenerator.java:
+	  Fix i18n of chart title. Set subtitle to expected value, pass facets
+	  description to StyledSeries to see expected legend.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics, resolved refactoring todo.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java
+	  (compute, computeAdvance, computeFeed): Refactored, extracted method,
+	  resolving duplicate code and TODO.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix waterline "calculation" at given km. Chosen approach is
+	"head-through-wall".
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Some refactoring to do the same calculation twice easier and be able
+	  to "cross" waterline against correct profile data.
+
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix setting of kilometer for profile (not yet waterlevel) of cross section
+	diagram.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  (getCrossSectionData): Respect corss_section.km data; do naive linear
+	  search for profile data for this km.
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java:
+	  Declare a ComputeType.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Implement computeFeed.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Minor cosmetics.
+
+2011-09-07  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java,
+	  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/state/DefaultState.java:
+	  Cosmetics, docs.
+
+2011-09-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/mapserver/barrier_polygons_class.vm,
+	  doc/conf/mapserver/barrier_lines_class.vm,
+	  doc/conf/mapserver/wsplgen_class.vm: Default Mapserver styles for
+	  barriers and WSPLGEN results. Those styles are only used as long as we
+	  don't have map specific themes (as already used in charts).
+
+	* doc/conf/mapserver/layer.vm: Implements a fallback mechanism for styling
+	  barrier lines/polygons and WSPLGEN results.
+
+	* src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: Added a
+	  getStyle() method that currently returns "null". This method needs to be
+	  implemented when map themes are introduced.
+
+2011-09-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: Renamed
+	  some attributes to make their job in the mapfile more obvious.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added
+	  new facet type for barriers.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: Add
+	  a facet for the barriers layer.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Parse
+	  barriers (lines and polygons) and create two new layers for each type -
+	  those layers are grouped.
+
+	* doc/conf/mapserver/layer.vm: Renamed attribute based on changes in
+	  LayerInfo and added support for Group-Layers.
+
+2011-09-06  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* doc/conf/conf.xml:
+	  Added driver to database configuration for use with postgresql.
+
+2011-09-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/mapserver/mapfile.vm: Removed FONTSET attribute and set quotes
+	  for SHAPEPATH.
+
+2011-09-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java:
+	  Removed. We gonna use the WMSLayerFacet until now.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Add a WMSLayerFacet after we triggered the Scheduler to start a WSPLGEN
+	  calculation.
+
+2011-09-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/mapserver/layer.vm,
+	  doc/conf/mapserver/mapfile.vm: Small bugfixes and style improvements.
+
+	* src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Trigger the
+	  MapfileGenerator after a WSPLGEN job has finished regardless if it has
+	  been finished successfully or not.
+
+2011-09-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: Added Apache Velocity 1.7 for templating support.
+
+	* doc/conf/conf.xml: Added config options for mapserver/template relevant
+	  stuff.
+
+	* doc/conf/mapserver/mapfile.vm,
+	  doc/conf/mapserver/layer.vm: New. A default mapfile template and a
+	  template used for layers.
+
+	* src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: New.
+	  This class is used while reading WMS layer relevant information from
+	  filesystem.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: New. This
+	  thread is used for creating mapfiles for Mapserver. The MapfileGenerator
+	  runs in daemon mode (own thread) and creates mapfiles based on WMS
+	  layer relevant information read from filesystem.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java,
+	  src/main/java/de/intevation/flys/utils/FLYSUtils.java: Moved shapefile
+	  specific XPath expressions from FloodMapState to FLYSUtils which is a
+	  better place to use it in other classes (as MapfileGenerator).
+
+2011-09-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java:
+	  Bugfixed broken attribute assignment.
+
+	* src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Call
+	  CallContext.afterBackground() after a WSPLGEN job has finished to remove
+	  the background lock from Artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Call CallContext.afterCall(BACKGROUND) to lock the Artifact for
+	  background processing.
+
+2011-09-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: New. This
+	  class is used to start WSPLGEN for a specific WSPLGENJob. The System
+	  property "wsplgen.bin.path" tells the JobExecutor where the WSPLGEN
+	  binary is placed (which means in general, the property points to the
+	  'wsplgen.exe').
+
+	* src/main/java/de/intevation/flys/wsplgen/Scheduler.java: New. This
+	  scheduler currently allows to start just a single WSPLGEN Thread. All
+	  WSPLGEN calculations should be started using Scheduler.addJob().
+
+	* src/main/java/de/intevation/flys/wsplgen/JobObserver.java: New. This
+	  thread reads log messages from WSPLGEN and listens for specific
+	  messages. It should be used to update status messages of the WSPLGEN
+	  calculation that is currently running.
+	  There is a System property that tells the JobObserver to log all WSPLGEN
+	  output to log4j: enable WSPLGEN output with "-Dwsplgen.log.output=true".
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Added
+	  the FLYSArtifact, the current working directory and the CallContext.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Use the Scheduler to start new WSPLGEN calculations.
+
+2011-09-02  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Add CrossSectionInfoGenerator.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionInfoGenerator.java:
+	  New, trivial implementation of CrossSectionInfoGenerator.
+
+	* doc/conf/conf.xml:
+	  Register CrossSectionInfoGenerator.
+
+2011-09-01  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cleanups of CrossSection*.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  New methods to retrieve name of utilized CrossSection.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Cleanup, get rid of copied unused method, documentation and more sensible
+	  translations.
+
+	* src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_de.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages.properties:
+	  Added cross_section* translations, also cleanups.
+
+2011-09-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Set the "typ" attribute of lines and polygons in barrier shapefiles.
+
+2011-09-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Set the Z values of line and polygon barrier geometries. Both barrier
+	  shapefiles will contain 3D geometries now.
+
+2011-09-01  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Get real data to display in CrossSection (although ignorant of
+	parameterization), making use of the showcase code of the CrossSectionApp-
+	Standalone application.
+
+	* doc/conf/artifacts/winfo.xml: Add new facet (~waterline) to state/out.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  New methods to get relevant data. So far just takes the first value of
+	  everything and assuming a waterlevel at 130m.
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java:
+	  New Facet responsible of water level in cross section.
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  Made some functionality publicly and statically available.
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java:
+	  Update call.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added new
+	  Facet type.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Added new Facet to out.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Respect new facet and facets data.
+
+2011-09-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: Close an open
+	  shapefile transaction and catch exceptions which are thrown while
+	  shapefile creation here. If there occured an error, this functions
+	  returns FALSE, otherwise TRUE.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Removed exception handling while shapefile creation - this is done in
+	  GeometryUtils now.
+
+2011-09-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Renamed the file for WSPLGEN required waterlevels to "waterlevels.wst".
+	  WSPLGEN did not work with the former "waterlevels.txt" file.
+
+2011-09-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: New static
+	  function that builds new SimpleFeatureTypes with additional attributes.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Write attributes "ELEVATION" and "KILOMETER" into the crosssection
+	  tracks shapefiles.
+
+2011-08-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a method
+	  stub that should return a FLYSArtifact based on a given UUID.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Write the selected WST file for WSPLGEN. Note, that this is the WST file
+	  of the current WINFO artifact. Furthermore, there is currently no way
+	  for the user to select a column from WST file, so we currently use the
+	  column that is written to WST file at first.
+
+2011-08-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed
+	  the parameter order of a compute(...) method. This makes me able to call
+	  this without a hash value.
+
+	* 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/ReportFacet.java:
+	  Adapted the parameter order of the compute() call (see above).
+
+2011-08-31  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	More bones to CrossSection sceleton.
+
+	* src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java:
+	  New, yet trivial implementations of Factory and Facet for CrossSections.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Add new CrossSection- (instead of Default-)Facet.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Adjusted to use (touch) new Factory and Facet.
+
+2011-08-31  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Cosmetics.
+
+2011-08-31  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Cosmetics.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java,
+	  src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  Removed obselete imports.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Removed obselete imports, whitespaces.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Whitespaces, docs.
+
+2011-08-31  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Fix translations of Main Values Facets.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Do acrobatics to keep state transient but get translated title.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesQFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesWFacet.java:
+	  Changed constructor to get description (which is then already be translated)
+	  dynamically, adjust deepCopy.
+
+	* src/main/java/de/intevation/flys/states/StaticState.java:
+	  Adjust constructor accordingly.
+
+2011-08-31  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Add sceleton for CrossSection outs.
+
+	* doc/conf/artifacts/winfo.xml:
+	  Added new output mode to respective state of winfo artifact configuration.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Add new Dummy-Facet in state.
+
+	* doc/conf/conf.xml:
+	  Registered new OutputGenerator.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added new CROSS_SECTION type.
+
+	* src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java:
+	  New, stubby skeleton for an CrossSectionGenerator.
+
+	* src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_de.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages.properties:
+	  Added cross_section translation, also cleanups (e.g. main values).
+
+2011-08-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Marked
+	  required parameters with a comment.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Export floodplains (german 'Talaue') to shapefile and write its file
+	  path into the WSPLGEN job.
+
+2011-08-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Search for a DGM that fits to the current river and km range and write
+	  its file path into the WSPLGEN job.
+
+2011-08-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: Improved
+	  exception handling: exceptions are catched in GeometryUtils now.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Write river axis and crosssections to shapefiles and save shapefile
+	  pathes in WSPLGENJob.
+
+2011-08-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Use the coorect SRID for reading GeoJSON and writing line/polygon
+	  shapefiles.
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: Use a
+	  concrete coordinate system while feature type creation.
+
+2011-08-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: Added GeoTools 2.7.2 dependencies for Shapefile, GeoJSON and
+	  EPSG support.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Store
+	  'LIN' parameter in a list now. A WSPLGEN parameter might contain many
+	  LINs.
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: New functions
+	  to create FeatureTypes and to write shapefiles.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Write user specified barriers into a shapefile placed in the artifact
+	  directory.
+
+2011-08-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a function
+	  that extracts the SRID defined in the global configuration file for a
+	  given river.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Removed the code that extracts the river SRID - use FLYSUtils instead.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver.
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java:
+	  Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Commit accidentally omitted result of refactoring (WINFO/FLYSUtils/getRiver).
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Removed implementations of getRiver, update calls to use FLYSUtils.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Interpolate Q main values, generate interpolated W main values on the fly from
+	Q main values.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  (getGaugeDatum): removed, obsolete
+	  (getLocation): new, gets location
+	  Use WstValueTable to look up interpolated Qs of MainValues. In absence of
+	  the same functionality for Ws, generate W Main Values from Q Main Values.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Use new helper class FLYSUtils, minor refactorization.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Use new functionality of helper class, convenience of FLYSArtifact.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Refactored to use new Helper class FLYSUtils, moved getRiver-functionality
+	in there.
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java (getRiver):
+	  New function to retrieve river of an artifact, slightly modified from
+	  WINFOArtifact.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Removed implementations of getRiver, update calls to use FLYSUtils.
+
+2011-08-29  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Minor cosmetics.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Resolved a TODO.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Added some documentation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java:
+	  Minor cosmetic.
+
+2011-08-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data.xml: Made it Oracle compatible.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java:
+	  If running in debug mode log executed statements.
+	  Helps debugging Oracle connections.
+
+2011-08-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Set some WSPLGENJob parameters which are stored at FLYSArtifact.
+
+2011-08-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/FLYSUtils.java: New. This helper
+	  class should provide some basic FLYS stuff. Currently, there are functions
+	  that return the km range/location.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed
+	  the methods that return the km range/locations. This is implemented in
+	  FLYSUtils now.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java,
+	  src/main/java/de/intevation/flys/exports/ChartGenerator.java: Adapted
+	  the WINFO method calls to retrieve the km range/locations - call
+	  FLYSUtils now.
+
+2011-08-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New method
+	  to destroy a single state.
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Cosmetic, remove debug output and comments, minor style adjustments.
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added limited themeing-support for MainValues.
+
+	* doc/conf/themes.xml:
+	  Added Q/W-MainValues themes.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Added limited theming support, add legend entry for main values.
+
+2011-08-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Added a configuration node that points to the directory
+	  where shapefiles should be stored in.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: New.
+	  This class is used to save/write the parameter for a WSPLGEN calculation.
+	  WSPLGEN's *.par files are written using the toFile() method.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  A directory for all WSPLGEN stuff is created in computeAdvance() - those
+	  directory and all its contained files are removed in endOfLife().
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java:
+	  New Util to work with theme-related stuff.
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Use NamedDoubles instead of MainValues, try to adjust scale of Ws.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Use NamedDoubles instead of MainValues, generalize annotation handling, to
+	  allow easier reusability and themeing.
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Added convenience constructor, exemplary switch on bordered text.
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* doc/conf/meta-data.xml:
+	  Recommend MainValues for Computed discharge curves.
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	New NamedDouble class which implements a <String,double>-pair.
+
+	* src/main/java/de/intevation/flys/artifacts/model/NamedDouble.java:
+	  New, implementation of a double with a string or vice versa.
+
+2011-08-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Call
+	  State.endOfLife() for all States when endOfLife() of the Artifact is
+	  called.
+
+2011-08-26  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Also plot "W"-MainValues (on vertical axis), take correct parameters, but
+	do not convert to correct scale (cm vs NN+m).
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Naive attempt at allowing the vertical axis to be sticked at.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Store Q and W MainValues separately, add them to plot as annotations.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Serve the MainValues, parameterized on river and location, Q and W.
+	  Removed Facet-implementation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Updated Facet Types.
+
+	* src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java:
+	  src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java:
+	  New, trivial facets, extracted from MainValuesArtifact.
+
+2011-08-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Call
+	  State.endOfLife() of each State that is no longer in the queue of the
+	  artifact when this artifact steps back to a previous state.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Plot MainValues delivered by MainValuesFacet in much the same ways than
+	  Annotations in LongitudinalSection plots.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Prepare further differentiation between Annotations that stick to X or Y-
+	  Axis, copied some positioning logic into StickyAxisAnnotation
+	  implementation.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Minor cosmetics.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added MainValue-Fetching-Capabilities to MainValuesArtifact.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java
+	  (initialize, getMinValues):
+	  Let MainValuesArtifact return "real" MainValues, although ignorant of all
+	  parameterization.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java
+	  (MainValueFacet):
+	  Improved and straightened implementation, added code-Annotations and Todos.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java
+	  (MainValueFacet):
+	  Improved and straightened implementation, added code-Annotations and Todos.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java
+	  (getState):
+	  Resolved multiple creation of state (yet not very clean).
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Cosmetics.
+
+2011-08-25  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added new (MainValues) Facet-Type.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java:
+	  Added a method that returns a River object based on its database id.
+
+	* src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: The
+	  data that is required for this artifact is fetched from database instead
+	  from a Master-Artifact. The creation of static artifacts should use
+	  database ids instead of cloning a Master-Artifact.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Removed needless imports.
+
+2011-08-24  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added configuration for factory and rule for MainValueArtifacts.
+
+	* doc/conf.xml:
+	  Add a mainvalue factory to serve MainValueArtifacts.
+
+	* doc/conf/meta-data.xml:
+	  Recomment mainvalue artifact when computed_discharge_curve can be put out.
+
+2011-08-24  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Let MainValuesArtifact have a state, outputmode and facet.
+
+	* src/main/java/de/intevation/flys/artifacts/states/StaticState.java:
+	  New, a non-abstract DefaultState.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Progressed with implementation, use StaticState to hook output modes and
+	  facet in; use (Static)FLYSArtifact implementation.
+
+2011-08-24  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Smaller cosmetics.
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Cosmetics in comments.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Added @Override annotations.
+
+	* src/main/java/de/intevation/flys/artifacts/model/DataFacet.java:
+	  Use brackets to improve readability of ternary operator.
+
+2011-08-24  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data.xml: Use "ids" in user specific part, too.
+
+2011-08-24  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data.xml: s/db-ids/ids/g to unify system and user specific
+	  loading.
+
+2011-08-24  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Extracted access to state and states in order to have fewer places to
+	  modify when going for single/trivial state- artifacts.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java:
+	  Append the river's srid to the WMSLayerFacet.
+
+2011-08-24  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Minor fixes, ressurect Facet implementation as inner class.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Moved the input of barriers one state
+	  earlier where the scenario is selected as well.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java:
+	  This state now desires the "map_digitize" UI provider and returns both
+	  items "scenario" and "uesk.barriers" in the dynamic describe part.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Removed the UI provider and the computeFeed() which is no longer needed,
+	  because there is no more user input in this state.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Repaired broken xpath expressions (the config changed in one of the last
+	  commits) and make use of the variable support in xpath expressions to
+	  replace the rivername.
+
+	* src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java:
+	  Save the rivername while initializing this artifact. This is now
+	  necessary, because each river can have its own background wms
+	  configured.
+
+2011-08-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Repaired broken river-wms initialization.
+
+2011-08-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java,
+	  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/ReportFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java,
+	  src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java:
+	  Made facets cloneable with the right type.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Removed superfluous imports.
+
+2011-08-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Declare the first artifact of an output as master artifact (artifacts
+	  are ordered by their creation time).
+
+2011-08-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  Removed facet interface.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Make artifacts cloneable. TODO: Override deepCopy() in subclassed
+	  states and facets.
+
+2011-08-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Added the MapInfoService and adapted the floodmap
+	  configuration. Now, each river can have its own background wms layer
+	  defined.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java:
+	  New. This service returns some basic information used to create maps for
+	  a specific river. The name of the desired river needs to be defined at
+	  "/mapinfo/river/text()".
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Adapted an xpath expression that points to the srid of a river in the
+	  floodmap configuration (which changed).
+
+2011-08-22  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Build out/facet filter from XML document passed at creation time.
+
+2011-08-22  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Added some code to filter outs/facets by an optional positive list.
+	  This is needed to only expose parts of the facets. This
+	  is needed for artifacts which are loaded into a collection.
+	  TODO: create the filter from the XML document passed at creation
+	  time.
+
+2011-08-22  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Moved all
+	  WINFO specific code to WINFOArtifact. FLYSArtifact is now only revolving 
+	  about state affairs.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Re-inserted
+	  the specific stuff here.
+	  
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java,
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ChartGenerator.java:
+	  Adjusted the casts.
+
+2011-08-22  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data.xml: Filter by outs in user template part.
+
+2011-08-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Defined an input value for the GeoJSON
+	  string to save user defined barriers in the map.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  Added a computeFeed() method. In addition, this state now prefers the
+	  "noinput" UI provider.
+
+2011-08-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Each river requires a SRID definition. This
+	  definition is used to transform the river's geometries into the desired
+	  projection.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java:
+	  This facets are able to save a SRID.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  The extent that is written into the WMSLayerFacet is determined by the
+	  boundary of the geometry.
+
+	* src/main/java/de/intevation/flys/utils/GeometryUtils.java: New. A
+	  utility class that provides helper functions for geometries. Currently,
+	  one function is defined, that creates a boundary string for OpenLayers.
+
+2011-08-19  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RangeState.java:
+	  Removed superfluous imports.
+
+2011-08-19  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Removed CallContext from state validation. It is not needed and hindered
+	  the extraction of all out of an artifact if you don't have a
+	  call context (like initial scan of datacage database).
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Extract all outs now.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java,
+	  src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DGMSelect.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/ProfileDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java,
+	  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/WQSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RangeState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Adjusted calls.
+
+2011-08-19  Felix Wolfsteller <felix.wolfsteller@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 lacalization of "Streckenfavoriten".
+
+2011-08-19  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Picky white-space cosmetics.
+
+2011-08-19  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added stub implementation of new MainValuesArtifact.
+
+	* src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java:
+	  New. Stub implementation for new MainValuesArtifact.
+
+2011-08-19  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fixed flys/issue262
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  Unified user and system tempate.
+	  Looks for "/artifact-database/metadata/template/text()"
+	  in conf.xml. Defaults to "${artifacts.config.dir}/meta-data.xml". If user id is
+	  given its the default connection for contexts.
+
+	* doc/conf/conf.xml: Adjusted
+	* doc/conf/meta-data-system.xml, doc/conf/meta-data-user.xml: Deleted.
+	* doc/conf/meta-data.xml: Unified version of user and system template.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java:
+	  Adjusted the test program.
+
+2011-08-18  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fixed flys/issue260
+
+	* doc/conf/meta-data-user.xml: Uses master_artifacts view now.
+
+2011-08-18  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Added view master_artifacts to select
+	  the master artifacts of the collections more easily.
+	  To upgrade existing database:
+
+	    CREATE VIEW master_artifacts AS
+	        SELECT a2.id             AS id,
+	               a2.gid            AS gid,
+	               a2.state          AS state,
+	               a2.creation       AS creation,
+	               ci2.collection_id AS collection_id
+	        FROM   collection_items ci2 
+	               JOIN artifacts a2 
+	                 ON ci2.artifact_id = a2.id 
+	               JOIN (SELECT ci.collection_id AS c_id, 
+	                            MIN(a.creation)  AS oldest_a 
+	                     FROM   collection_items ci 
+	                            JOIN artifacts a 
+	                              ON ci.artifact_id = a.id 
+	                     GROUP  BY ci.collection_id) o 
+	                 ON o.c_id = ci2.collection_id 
+	        WHERE  a2.creation = o.oldest_a;
+
+	 TODO: Use the view in the templates.
+
+2011-08-18  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data-user.xml: Removed state filter because it was broken.
+	  Simplified by joining two contexts.
+
+2011-08-18  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  Fix issues with lines of annotation when zoomed (wrong scale used).
+
+2011-08-18  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added legend for annotations to LongitudinalSectionDiagram.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Added a pseudo-dataseries/collection to employ existing infrastructure for
+	  displaying localized label for Annotations (yet unthemed).
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties:
+	  Made label-string available for localization.
+
+2011-08-17  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Moved StickyAxisAnnotation into new package de.intevation.flys.jfree .
+
+	* src/main/java/de/intevation/flys/exports/StickyAxisAnnotation.java:
+	  Deleted/moved to src/main/java/de/intevation/flys/jfree/ .
+
+	* src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java:
+	  New/moved from src/main/java/de/intevation/flys/export/ , adjusted
+	  package statement, made class public.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Added import statement for de.intevation.flys.jfree.StickyAxisAnnotation .
+
+2011-08-17  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Extracted and renamed CustomAnnotation to StickyAxisAnnotation. Also removed
+	needless imports.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/StickyAxisAnnotation.java:
+	  Extracted class implementation CustomAnnotation and renamed to
+	  StickyAxisAnnotation.
+
+2011-08-17  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Improved CustomAnnotations and rendering thereof, now including an
+	"axis mark" (little line at axis), also prepared possibility to put
+	annotations on Y-axis.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Improved CustomAnnotation to include marks on the axis and better spacing
+	  from it.
+
+2011-08-17  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Implemented proof-of-concept collision-detection when drawing
+	CustomAnnotations (text only).
+
+	* src/main/java/de/intevation/flys/exports/ChartExportHelper.java:
+	  Pass a fresh ChartRenderingInfo-Object to createBufferedImage, such that
+	  information can be collected while rendering.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Exploit the fact that XYTextAnnotation already registers drawn shape in the
+	  ChartRenderingInfo if it exists and either an URL or tooltip is set.
+	  Before drawing, calculate own shape and compare against already registered
+	  shapes.
+
+2011-08-16  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Implemented (yet dummy) custom Annotation class.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Added implementation of yet dummy CustomAnnotation class.
+
+2011-08-16  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Slightly improved rendering of annotations.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Slightly improved rendering of annotations. Still no valid collision
+	  detection. Annotations are drawn every 2 km; first come first serve.
+
+2011-08-15  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue191
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQ.java(longestIncreasingWRangeIndices):
+	  Added a method to find the longest index range with increasing w values.
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java: Export the longest
+	  range of monotone increasing w values instead of the first one.
+	  TODO: The first line of the export is still broken.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Removed superfluous import.
+
+2011-08-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java:
+	  Resolved two TODOs: get Annotations of selected River, get "point"
+	  Annotations only.
+
+2011-08-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added functionality to query range and point-annotations only to
+	AnnotationFactory.
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java
+	  (getPointAnnotations, getAnnotationsBreadth):
+	  New functions to query breadth and point-only annotations.
+
+2011-08-11  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  Added a development mode for recommendations. Enabled with
+	  stetting system property 'flys.datacage.recommendations.development' to true.
+	  When set the XML template are re-read if the timestamps of the
+	  files have changed so you do not have to restart the server again and again.
+
+	* doc/conf/meta-data-user.xml: Sort collections by creation time in descending order.
+
+2011-08-11  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Rather picky cosmetics only.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WQKms.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DefaultState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  End comments on a full stop, separate from closing '*/' by whitespace,
+	  adjusted javadoc comments.
+
+2011-08-11  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Made Annotations visible in LongitudinalSection diagrams.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Register new Facet Type, let LongitudinalSectionGenerator include
+	  Annotations in diagram (yet unfiltered and independent of river).
+
+2011-08-11  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* doc/conf/conf.xml,
+	  doc/conf/artifacts/annotation.xml,
+	  doc/conf/meta-data-system.xml:
+	  Added configuration for AnnotationArtifacts.
+
+2011-08-11  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Added demo- implementation of a AnnotationArtifact and its Facet.
+
+	* src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/states/AnnotationRiverState.java:
+	  New. Initial version of an AnnotationArtifact and its State and Facet.
+
+2011-08-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Extended schema for artifacts and collections
+	  to have creation times, too.
+
+	  To update existing databases:
+
+	    ALTER TABLE artifacts ADD COLUMN creation TIMESTAMP NOT NULL DEFAULT current_timestamp;
+	    ALTER TABLE collections ADD COLUMN creation TIMESTAMP NOT NULL DEFAULT current_timestamp;
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Store creation times for artifacts and collections, too.
+
+	* src/main/resources/datacage-sql/org-h2-driver.properties,
+	  src/main/resources/datacage-sql/org-postgresql-driver.properties:
+	  Adjusted SQL statements.
+
+2011-08-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Make it compilable again (BackendListener interface changed).
+
+2011-08-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data-user.xml: Added grouping element around w/q of each
+	  longitudinal section artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java:
+	  Added some debugging capabilities.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java:
+	  Added isEmpty() method.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java:
+	  Now it is possible to nest <dc:elements> into other elements in the <dc:context>
+	  body. This is useful and needed for grouping and repeating results.
+
+2011-08-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  User connection was cached, system was not. Lead to incorrect results.
+
+2011-08-09  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data-user.xml: Added <old_calculations> element
+	  around old calculations.
+
+2011-08-09  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java:
+	  Cosmetic: Replaced usage of legacy java.util.Stack with java.util.Deque.
+
+2011-08-04  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data-user.xml: Use 'CAST(x AS uuid)' instead of 'x::uuid'
+	  to be more compatible.
+
+2011-08-04  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  If given an artifact place its identifier into parameters passed to template.
+	  Fixed swapped user/system connections if using the user template.
+
+	* doc/conf/meta-data-user.xml: Recommend w/q facet from old calculations
+	  if an artifact was given that represents a longitudinal section
+	  "Laengsschnitt".
+
+	  TODO  I: The template uses PostgreSQL specific UUID casts.
+	  TODO II: We need to find a way only to recommend the master artifacts.
+
+2011-08-04  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java:
+	  Added a <dc:comment> tag to place comments in the meta data templates.
+	  <!-- ... --> comments are copied through.
+
+	* doc/conf/meta-data-user.xml: Added a simple test.
+
+2011-08-04  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Be a bit more tolerant about empty strings for UUIDs of artifact and user.
+
+2011-08-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Bring user specific meta data service to life.
+
+	* doc/conf/conf.xml: There are now two templates to configure:
+	  The system template (only the data from the backend) and the
+	  user template (the datcage db and the backend db),
+
+	* doc/conf/meta-data-template.xml: Deleted.
+	* doc/conf/meta-data-user.xml: New. The user specific template. TODO: Write it!
+	* doc/conf/meta-data-system.xml: New. The system template.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java:
+	  Deleted.
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  New. The logic to fill the templates.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java:
+	  Adjusted to follow the new call signatures.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Refactored. Removed the old code and only leave the new service. Following
+	  XPaths are evaluated on the incomming document:
+
+	  "/art:meta/art:artifact-id/@value" The UUID of the artifact. Optional.
+	                                     Used to fill the template enviroment.
+	  "/art:meta/art:user-id/@value"     The UUID of the user. Optional.
+	                                     If given the user specific template is filled.
+	  "/art:meta/art:outs/@value"        The list of outs used to recommend for the
+	                                     various outputs.
+	  "/art:meta/art:parameters/@value"  A list of key/value pairs to inject more
+	                                     filters to the templating.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java:
+	  Change to follow the new recommendations semantics.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java:
+	  Added symbolic constants to distinguish "user" and "system" db connections.
+
+2011-08-03  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* doc/conf/conf.xml:
+	  Cosmetics, let comments start with a capital and end on a full stop,
+	  removed incorrect comment.
+
+2011-08-03  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Cosmetics, let comments start with a capital and end on a full stop.
+
+2011-08-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java(extractOutputNames):
+	  Fixed potential NPE.
+
+2011-08-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Added support for more than one db connection in datacage templating.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java:
+	  Now you can pass a list of named db connections to the build process.
+	  The purpose is to mix more then one database (e.g. the backend db and
+	  the user specific one).
+
+	  To use this feature you can add an "connection" attribute
+	  to <dc:context> with the name of the connection to use.
+	  If no connection name is given the last used is used again.
+	  Initially the first connection in the given list is used. 
+	  If the context is left the connection that was active before 
+	  will be active again in a stacking manner.
+
+	  When creating NamedConnection objects you can set a boolean flag
+	  if the results coming from the connection should be cached. This
+	  is useful e.g. for the user specific database which runs in-memory
+	  so caching would introduce some superfluous overhead.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java:
+	  When executing the queries explicitly pass if caching should be used.
+
+2011-08-02  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Forgot to fetch dialect dependent SQL statement for deleting
+	  artifacts by uuid.
+
+2011-08-02  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Set the name of the collections at initial scan, too.
+
+2011-08-02  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Fixed wrong SQL references.
+
+2011-08-02  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Fixed two NPEs.
+
+2011-08-02  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java,
+	  src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Forwarded kill collections and artifacts events to datacage.
+	
+	* src/main/resources/datacage-sql/org-h2-driver.properties,
+	  src/main/resources/datacage-sql/org-postgresql-driver.properties:
+	  Added statement to delete artifact by uuid.
+
+2011-08-02  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java:
+	  Made it compilable again. The signature of BackendListener has changed.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Completed the backend listener stuff.
+
+	  TODO  I: Added some cleanup for orphaned artifacts.
+	  TODO II: Figure out a way to delete collections/artifacts
+	           which are delete from backend without the 
+			   backend API.
+
+	* src/main/resources/datacage-sql/org-h2-driver.properties,
+	  src/main/resources/datacage-sql/org-postgresql-driver.properties:
+	  Added needed statements.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Update collection names on change. Remove artifacts from collections.
+
+	* src/main/resources/datacage-sql/org-h2-driver.properties,
+	  src/main/resources/datacage-sql/org-postgresql-driver.properties:
+	  Added needed statements.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java:
+	  Call datacage with the global context. This is needed to access the state engine.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: Changed
+	  signatures to take the global context, too. Create artifacts via backend listener
+	  interface.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java:
+	  Fixed recursion bug.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java:
+	  Added debug output.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/conf.xml: Added backend listener for datacage.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java:
+	  New. Proxies backend listener calls to datacage.
+	 
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Implements backend listener. TODO: Update the datacage database
+	  according the change calls.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating:
+	  Moved/renamed package to better fit the common semantics.
+	  DataCage.java is now call NoneUserSpecific.java to reflect the
+	  fact that it is the template for the user independent db
+	  analysis.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java,
+	  src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Ajusted imports and calls.
+
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/conf.xml: For documentation purposes added a out-commented 
+	  section with the default configuration of the datacage.
+
+2011-07-31  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Delete the artifacts at before initial scan, too. They are independent
+	  from users. Fixed problem when writing state data content.
+
+	* src/main/resources/datacage-sql/org-h2-driver.properties: Added
+	  statement to delete the artifacts at initial scan, too.
+
+	* src/main/resources/datacage-sql/org-postgresql-driver.properties:
+	  New. PostgreSQL version of the statements. The database scheme
+	  is the same as H2. Very useful for debugging.
+
+2011-07-31  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Added kludge for the types of artifact data. They seem to be null
+	  in some circumstances. Needs to be debugged!
+
+2011-07-31  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java:
+	  Fixed default connection URL to use a namend in-mermory database.
+
+2011-07-31  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Fixed constraint.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Added some debug output.
+
+2011-07-29  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Removed artifact_id from facet because there
+	  is a link via out_id -> outs.id: artifact_id -> artifacts to
+	  find the corresponding artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties: Store
+	  facets of outs into datacage db at initial scan.
+
+2011-07-29  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties:
+	  Store outs of artifacts into datacage db at initial scan.
+	  TODO: store facets.
+
+2011-07-29  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Added 'type' column in artifacts data.
+	  Maybe useful for filtering.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties:
+	  Store artifact data into db at initial scan.
+	  TODO: store outs and facets.
+
+2011-07-29  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties:
+	  Add artifacts into datacage db at initial scan.
+	  TODO: Store data, outs and facets.
+
+2011-07-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Fixed spelling in sequence name.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties: Simply add
+	  collection item at initial scan if artifact was stored before.
+	  TODO: Store new artifacts.
+
+2011-07-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties: Add
+	  collections at initial scan.
+
+2011-07-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties: Add users
+	  at initial scan.
+
+2011-07-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Using sequences for id generation now
+	  to make schema more compatible.
+
+2011-07-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java,
+	  src/main/resources/datacage-sql/org-h2-driver.properties: Clear database
+	  before initial scan.
+
+2011-07-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DBConnection.java:
+	  Deleted. This stuff comes from the artifact database now.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java:
+	  New. The db config of the datacage database.
+
+	* src/main/resources/datacage-sql/org-h2-driver.properties: New.
+	  The SQL statements needed for the datacage.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  Make use of the db config.
+
+2011-07-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java:
+	  New. A artifact database lifetime listener to build the initial
+	  index of the artifacts in database.
+
+	* doc/conf/conf.xml: Added the datacage to the list of lifetime
+	  listeners.
+
+2011-07-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Added an explicit table for the outs
+	  of an artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Make the current outs of an artifact accessible only with
+	  the global context.
+	
+2011-07-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: Added ON DELETE CASCADE constraints.
+	  Added state in facet.
+
+2011-07-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/datacage.sql: New. H2 Schema for the datacage database.
+	  Uses special features like IDENTITY (autoincrement) typed columns.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/DBConnection.java:
+	  Pooled connection.
+
+	* pom.xml: Added dependencies to H2 and Apache DBCP.
+
+2011-07-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/test/java/de/intevation/flys/artifacts/AppTest.java,
+	  src/main/java/de/intevation/flys/artifacts/App.java: Removed.
+	  This the stupid "Hello, World!" app initially created by the
+	  maven archetype. It was never used.
+
+2011-07-25  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Argh! Usage the DOM was not thread safe (discovered with ab).
+
+2011-07-25  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/meta-data-template.xml: '$recommended' lead to XPath
+	  errors. Using "dc:contains($parameters, 'recommended')" helps.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java,
+	  src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java:
+	  Added some debugging.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java:
+	  Added parameters as 'parameters' to parameters. Usefull to check
+	  for containment of variables.
+
+2011-07-25  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Added 'if log.isDebugEnabled() {}'.
+
+2011-07-25  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Made Artifact UUID optional and accept extra parameters, too. This enables
+	  the service to be used without an arttifact and test all filters.
+
+	  <art:meta xmlns:art="http://www.intevation.de/2009/artifacts">
+	     <art:outs value="computed_discharge_curve,floodmap"/>
+	     <art:parameters value="river:Elbe"/>
+	     <art:filters value="recommended"/>
+	  </art:meta>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java:
+	  'contain' accept Maps and collection, too.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java:
+	  Made artifact option (= null) in recommendations.
+
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java:
+	  Moved connection specific into inner class. The enables the reuse of the
+	  compiled statement.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: The
+	  SQL statements are now only compiled once at creation time of the builder.
+	  Each connection now reuses them.
+
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/cache.xml: Added configuration for static datacage db access.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/App.java: Using
+	  caches seems to need an explicit System.exit().
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java:
+	  Added support for caching the SQL statements and there results.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: Some
+	  clean up. Reordered code for performance. Strip SQL statements more
+	  aggressively.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java:
+	  Made it Serializable.
+
+	* src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java:
+	  Introduced system property 'flys.artifacts.cache.config.file' to make
+	  the caching configurable without pulling up the whole stack.
+
+2011-07-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Prepared the FLYSArtifactCollection to support the storage of already
+	  loaded recommendations in its attribute document.
+
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Adjusted to use the DataCage recommendations. The incomming document
+	  can pass the artifacts UUID by '/art:outs/@value' the outs as a
+	  comma separated list in '/art:outs/@value' and optional a set of
+	  filters comma separated in '/art:filters/@value'.
+
+	  If UUID and OUTS are not given the old service is used. This
+	  should be removed as soon as the client uses the new service.
+
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java:
+	  Allow '-' in variable names.
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data-template.xml: Now, the river-id is really added to
+	  the factory node of the wmsbackground layer.
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data-template.xml: Added the river-id to the factory node
+	  of the riveraxis and wmsbackground layer.
+
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java,
+	  src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java,
+	  src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java,
+	  src/main/java/de/intevation/flys/artifacts/services/RiverService.java:
+	  Adjusted to implement changed Service interface.
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Refactored
+	  the method that retrieves the Outputs for the Artifact. Now, we are able
+	  to query the Outputs for the current state, and all outputs separately.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: The
+	  recommendations provided by this monitor will take the Outputs of the
+	  current state only into account.
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/meta-data-template.xml: Added conditions for each output type.
+	  Splitted the "floodmap" output into two parts: a recommended one and a
+	  complete one. The recommended part will only build the document tree for
+	  the recommended artifacts; the complete part will build the whole document
+	  tree that is available for a floodmap.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java:
+	  This Hook now uses the DataCage to generate the recommended artifacts.
+	  The output-defaults configurtion is needless now.
+
+	* doc/conf/output-defaults.xml: Removed. The configuration of recommended
+	  artifacts takes place in meta-data-template.xml.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java:
+	  Bugfix: the DataCage didn't start working if its builder was NOT null,
+	  but it shouldn't start if the builder IS null.
+
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Now it is possible to directly pass a Node as a root to the builder.
+	  The owning document if fetch by Node.getOwnerDocument(). This is
+	  useful if you want to generate the recommendation directly into
+	  an already existing document under a given node.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java:
+	  Changed the signature of recommend() to accept a node where to
+	  append the recommendations.
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Added a macro mechanism:
+
+	    <dc:macro name="keine-fuenf">
+	      <dc:text>'5' ist nicht in der Liste der Outs.</dc:text>
+	    </dc:macro>
+	    <dc:call-macro name="keine-fuenf"/>
+	    <dc:call-macro name="keine-fuenf"/>
+	    <dc:call-macro name="keine-fuenf"/>
+
+	  Macros can be defined everywhere in the template
+	  with 'macro'. There bodies can contain all valid elements
+	  including other 'macro's and 'call-macro's. They are
+	  called with their 'name' with 'call-macro'. The control flow
+	  is continued inside the body of the called macro and 
+	  will continue right after the calling 'call-macro' when
+	  the macro body is finished.
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Added a new 'if' construct similiar to XSLT:
+
+	    <dc:if test="not(dc:contains($outs, '5'))">
+	        <dc:text>'5' ist nicht in der Liste der Outs.</dc:text>
+	    </dc:if>
+
+	  The control flow is continued inside the 'if' if the 'test' attribute
+	  as an XPath expression on an empty document evalutes to true.
+	  Else the inside is skipped. There is no 'else'. Use 'choose'/'otherwise'
+	  if you need this.
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java:
+	  Added a recommend() method to generate recommendations for
+	  a given artifact, outs and extra parameters.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added
+	  method to extract all data at once.
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java:
+	  New. Custom XPath function provider. Provides
+	  'dc:contains(Object [] haystack, Object needle)' by now. Should be
+	  useful to check containments in 'out' lists later.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Register the FunctionResolver to the evaluated XPaths.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/App.java:
+	  Added code to parse
+	  "param:a,b,c" to "param" -> new String [] { "a", "b", "c" } to
+	  help testing the 'dc:contains' XPath function.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Added wms configurations for Saar, Mosel and Elbe.
+	  Each river supported by FLYS requires such a WMS configuration. A WMS
+	  should contain layers for the river axis, buildings, kilometer labels
+	  and maybe a background layer as well.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java:
+	  Added key that is used to store a map of WMS URLs - for each river a
+	  WMS URL.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Parse the river WMS from global configuration.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Create WMSLayerFacets with URLs based on the river and the river wms
+	  configuration stored in the FLYSContext.
+
+	* src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java:
+	  Adapted the initialize() signature and the method call of computeInit()
+	  which requires a FLYSContext to retrieve the river WMS configurations.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Added the context object parameter to the computeInit() method.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Call
+	  initialize() with the context object - which is a FLYSContext or a
+	  CallContext.
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/App.java:
+	  Check if builder was created properly before using it.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java:
+	  Implements now variable provider for XPath expressions.
+	
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Added new choose/when/otherwise construct similiar to XSLT
+
+	      <dc:choose>
+	         <dc:when test="$river = 'Mosel'">
+	             <dc:text>Es ist die Mosel.</dc:text>
+	         </dc:when>
+	         <dc:when test="$river = 'Saar'">
+	             <dc:text>Es ist die Saar.</dc:text>
+	         </dc:when>
+	         <dc:otherwise>
+	            <dc:text>Es ist weder Mosel noch Saar.</dc:text>
+	         </dc:otherwise>
+	      </dc:choose>
+
+	  A 'choose' block can contain a list of 'when's and an optional
+	  'otherwise'. For each 'when' the test attribute is evaluated
+	  as an XPath expression on an empty document. The result of
+	  the evaluation is taken as a boolean value. If its value is
+	  true the control flow is continued inside the corresponding
+	  'when' and the other choose elements are not tested.
+	  If the value is values the testing continues with the next
+	  'when'. If no 'test' expression is evaluated to true, the
+	  control flow continues inside the 'otherwise'. If no 'otherwise'
+	  is given nothing happens at all.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Added a config section for floodmaps. Currently, the
+	  background layer's url and layername is defined here.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java:
+	  Read the background layer configuration from conf.xml. Those values are
+	  used to create the WMSLayerFacet.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java:
+	  The background layer facet will no longer have an extent set.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java:
+	  Set the extent of the created WMSLayerFacets and i18n its descriptions.
+
+	* 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
+	  background an river axis layer.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java:
+	  This facet type has a method to set the extent of a WMS layer. The
+	  extent is written to the facets XML node in toXML() as well.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adapted
+	  the signature of setup() which requires a CallMeta parameter now.
+
+	* src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: Use
+	  the CallMeta object retrieved in setup() to call initialize(). It is now
+	  able to i18n things.
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/App.java: New.
+	  Standalone app to debug the datacage template. To use in a maven environment:
+
+	  -Dmeta.data.template=PATH_TO_META_DATA-TEMPLATE.XML \
+	  -Dmeta.data.parameters=river:Mosel \
+	  -Dmeta.data.output=OUTPUT.XML \
+	  -Dflys.backend.user=DB_USER \
+	  -Dflys.backend.password=DB_PASSWORD \
+	  -Dflys.backend.url=DB_CONNECTION_URL \
+	  -Dexec.mainClass=de.intevation.flys.artifacts.services.meta.App
+
+2011-07-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java:
+	  New. First step to factor out the "Datenkorb" logic into a service independent
+	  singleton.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Uses the "Datenkorb" singleton now.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java:
+	  Removed "index" property, because it is already existing in parent
+	  class.
+
+2011-07-19  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Improved error handling.
+
+2011-07-19  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/resources/metadata/template.xml: Deleted.
+
+	* doc/conf/meta-data-template.xml: New. Was template.xml
+
+	* doc/conf/conf.xml: Made meta data template configurable.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Load template from configuration not from resources.
+
+2011-07-19  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/resources/metadata/template.xml: s/[a-z]+-id/db-id/
+	  Make database ids identifiable with unique name "db-id".
+
+2011-07-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java:
+	  Override the toXML() method. Subclasses can now write their own XML
+	  representation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java:
+	  New. This ManagedFacet uses an Element (DOM) to store the information
+	  about a facet. The intent of this facet type is to represent a facet
+	  stored in an Collection attribute. Different facets can have different
+	  attributes that we need to parse, but the only thing ManagedFacets need
+	  to do, is to adjust the attributes "active" and "position". So, those
+	  values are set directly on the Element, the other attributes aren't
+	  touched.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java:
+	  New. This facet is a wrapper for another facet. This subclass of a
+	  ManagedFacet overrides the toXML() method. The XML representation is
+	  defined by the inner facet that is stored as member variable. The
+	  ManagedFacet specific attributes "artifact", "facet", "pos" and "active"
+	  are added manually.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java: Uses
+	  the toXML() method to write a facet node into the attribute document.
+
+	* src/main/java/de/intevation/flys/collections/AttributeParser.java: Uses
+	  the ManagedDomFacet to save the information of a facet which is
+	  contained in the attribute part of a Collection's DESCRIBE document.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java: Uses the
+	  ManagedFacetAdapter to save a facet, because we want to keep the
+	  specific facet to be able to write its specific XML representation into
+	  the Collection's DESCRIBE document.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adapted the XPath of facets stored in the attribute part of the
+	  DESCRIBE.
+
+2011-07-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a
+	  method that returns the Outputs for the Artifact.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adapeted the call of OutputParser.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java:
+	  Simplified the code to read the Outputs of Artifacts. This parser will
+	  now longer parse the DESCRIBE documents of the Artifacts, but query the
+	  Outputs via FLYSArtifact.getOutputs() directly.
+
+2011-07-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java:
+	  Adapted the function call of ProtocolUtils.appendOutputModes().
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java:
+	  Override toXML() to add the URL and layernames to the XML representation
+	  of this facet.
+
+2011-07-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/wmsbackground.xml,
+	  doc/conf/artifacts/riveraxis.xml: New configurations for an Artifact
+	  that is used as background layer in floodmaps, and an artifact that is
+	  used as layer showing the river axis in a floodmap.
+
+	* doc/conf/output-defaults.xml: New file to configure default artifacts
+	  for specific output states. E.g. the floodmap state recommends a
+	  background layer and a layer displaying the river axis. In suche case,
+	  the floodmap state recommends two artifacts for the two layers.
+
+	* doc/conf/conf.xml: Added new artifacts/artifact-factories and a Hook to
+	  monitor artifacts (-> CollectionMonitor.java).
+
+	* src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java:
+	  New. This facet is used to represent a layer in a map. So, this facet
+	  stores information about a WMS URL and the layer names provided by this
+	  WMS.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added
+	  facet types for the wmsbackground and riveraxis.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java,
+	  src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java:
+	  New. An artifact and its default state. The intent of these classes is
+	  to generate WMSLayerFacets which represent background layers in maps.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java,
+	  src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: New.
+	  An artifact and its default state. The intent of these classes is to
+	  generate WMSLayerFacets which represent layers that display a river
+	  axis.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Added the INIT ComputeType.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a
+	  case for the INIT ComputeType while computing data.
+
+2011-07-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Added a method computeInit() which is called to initialize data/facets
+	  after an artifact has been created.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Repaired
+	  broken XPath.
+
+2011-07-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java:
+	  Removed the code to generate new artifacts. Instead of creating new
+	  artifacts automatically, we decided to suggest creating new artifacts
+	  from specific types. Therefore, the DESCRIBE document of the artifacts
+	  is extended with a node that contains recommended artifact types.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  FLYSArtifacts might be setup with the identifier of an other
+	  FLYSArtifact. Subclasses are able to override a method called
+	  initialize(Artifact, GlobalContext). This might be helpful to extract
+	  required values or clone artifacts.
+
+2011-07-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Adapted the signature of createArtifactContext() - it returns an
+	  instance of GlobalContext now.
+
+2011-07-14  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Lines.java:
+	  Debugged the water fill algorithm. Added a lot of logging.
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  Added a text field to give a water level to fill in.
+
+2011-07-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Moved code
+	  to create the output modes based on the given facets to FLYSArtifact. In
+	  addition, FLYSArtifact got a new method that returns a specific input
+	  value as string.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: New.
+	  This hook monitors the "post-feed" and "post-advance". If the monitored
+	  Artifact's state has configured recommended artifacts, this hook will
+	  create new Artifacts.
+
+	  TODO: We have to add the UUIDs of the new Artifacts to the DESCRIBE
+	  document of the artifact to let the client know, that there are new
+	  recommended Artifacts.
+
+	* src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java: New.
+	  This Artifact is the base class for Artifacts, that represent static
+	  data. E.g. this could be a decoration theme in a chart or a background
+	  layer in the map.
+
+	* src/main/java/de/intevation/flys/artifacts/states/OutputState.java: New.
+	  This state might be used as base class for states, that doesn't require
+	  any user input, but only provide static Facets added by a computeFeed()
+	  operation. So, subclasses need to implement computeFeed() only.
+
+2011-07-13  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  New. Standalone Swing-App to test cross sections from database without the
+	  hassles of our complete software stack. Runnable from a maven environment:
+
+	  $ mvn -e \
+	    -Dflys.backend.user=DB_USER \
+		-Dflys.backend.password=DB_PASSWD \
+		-Dflys.backend.url=DB_CONNECTION_URL \
+		-Dexec.mainClass=de.intevation.flys.artifacts.charts.CrossSectionApp \
+		exec:java
+
+	  You can set the river to be used with the system property 'river'.
+	  Defaults to 'Mosel'.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java:
+	  Removed superfluous imports.
+
+2011-07-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: The FloodMapState has a new Outputmode
+	  called "floodmap" now.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java: New.
+	  This facet is used to generate WSPLGEN results.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added
+	  the WSPLGENFacet.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java:
+	  This state will now generate WSPLGENFacets.
+
+2011-07-12  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Lines.java:
+	  Fixed corner case.
+
+2011-07-11  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Lines.java: New.
+	  fillWater() generates a list of wet lines for a given profile and a
+	  given water level.
+
+2011-07-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The
+	  Outputs created while generating the DESCRIBE document will now have the
+	  'type' member set.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java: Read the
+	  'type' member from DESCRIBE document.
+
+2011-07-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Removed a typo.
+
+2011-07-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DataFacet.java:
+	  DataFacet are now able to store the ID of the state which has created
+	  this Facet.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Initialize DataFacets with the ID of this state. This is necessary to
+	  renew the waterlevel data if it is no longer existing in the cache.
+
+2011-07-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java: A
+	  report facet can now store the state's id and the artifact's hash value
+	  when it has been created.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Initialize the ReportFacet and WaterlevelFacet with state id and hash
+	  information. This has been necessary to retrieve reports and waterlevels
+	  in states after this one - in states that we need to enter for floodmap
+	  parameterization.
+
+2011-07-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java:
+	  Changed the desired UI provider.
+
+2011-07-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added the option to continue the
+	  waterlevel parameterization with the intent to create flood maps.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  This state is no longer a final state. The user has the option to
+	  continue with the parameterization for flood maps based on the current
+	  waterlevel. Therefore, this states desires the "continue" UI provider.
+	  Clients should recognice this to just step to the next state or display
+	  a button that lets the user step to the next state manually.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java,
+	  src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java:
+	  New. These states are used to parameterize a further calculation type:
+	  flood map.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Required strings for the
+	  flood maps states.
+
+2011-07-04  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix(?) for flys/issue114
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQ.java:
+	  Make guessing a bit more robust.
+
+2011-07-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Added the math needed to calculate "W-Differenzen" in "Laengsschnitten".
+	Needs testing!
+
+	* src/main/java/de/intevation/flys/artifacts/model/NamedObject.java:
+	  Made it an interface to be usable in more than one inheritance chain.
+
+	* src/main/java/de/intevation/flys/artifacts/model/NamedObjectImpl.java:
+	  Implements the NamedObject interface and is the new base class of
+	  WQ and WKmsImpl.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKms.java:
+	  New. Interface to associate kms with ws.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java:
+	  Changed the base class to NamedObjectImpl. Renamed getKms(int)
+	  to getKm(int) to make clear it return a single scalar value
+	  and fullfil the WKms interface.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java:
+	  New. Implements the WKms interface. Intended to be a lightweight
+	  datastore for "zusaetzliche Laengsschnitte" and as results
+	  of the WKmsOperations.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQ.java:
+	  Changed base class to NamedObjectImpl.
+
+	* src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java:
+	  New. Operations on WKms data.
+	  Currently only the SUBTRACTION operation is implemented. This
+	  one is needed to calculate the "W-Differenzen". The operation
+	  is insensitive about the km directions of the datasets. Missing
+	  values are interpolated linear.
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Adjusted to satisfy the signature change of WQKMs.
+
+2011-07-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java: New. This
+	  XYSeries stores the style information that should be used to render this
+	  series. These information are stored as raw XML documents. A public
+	  method can be used to apply those style information to a
+	  XYLineAndShapeRenderer.
+
+	  Note: The only two attributes currently supported by StyledXYSeries
+	  items are "linesize" and "linecolor".
+
+	* doc/conf/themes.xml: Added some more basic themes for the four
+	  calculation methods.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: This
+	  generator now tries to apply themes for all series contained in the
+	  chart. If a series is no instance of StyledXYSeries, the default
+	  renderer is used.
+
+	* 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/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Removed the code that had been introduced to adapt renderers statically.
+	  Now, each of these concrete ChartGenerators instantiates StyledXYSeries
+	  items to put the curves into the chart. Those items contain style
+	  information now!
+
+2011-07-01  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue135 (Diagramm: Trotz abgeschalteter Themen bleiben Beschriftungen bestehen)
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: The way
+	  to store datasets has changed. Until this revision, the concrete
+	  generators managed their own datasets. E.g. the
+	  DischargeLongitudinalSectionGenerator had three datasets: w, q and
+	  corrected w. Now, there are just two datasets, managed by this base
+	  generator - one dataset for the first Y axis and one dataset for the
+	  second Y axis. This makes it easier to remove axes, that have no data to
+	  be displayed. All concrete chart generators have to add their XYSeries
+	  using two methods: addFirstAxisSeries() and addSecondAxisSeries().
+
+	* 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/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Those concrete chart generators no longer manage datasets themself but
+	  they use the two methods described above, to plot the data to the first
+	  or second Y axis.
+
+2011-07-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java:
+	  Added the option to use a filter to reduce the number of items returned
+	  by this service.
+
+2011-06-30  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue159 (WINFO: Radiobutton - Ortsauswahl bei "W für ungleichwertigen Abflusslängsschnitt" entfernen)
+
+	* doc/conf/artifacts/winfo.xml: Changed the kilometer range input for
+	  calculation 4. This calculation type requires a kilometer range. So,
+	  after choosing the calculation 4, the transition model leads to a state
+	  that just allows the input of a kilometer range with no option to
+	  enter locations.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java:
+	  New. A base state for the kilometer selection for calculations. The
+	  target of this state is to provide facets for the duration curves.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java:
+	  New. This state is used to enter a kilometer range. The difference to
+	  the LocationDistanceSelect state is, that there is no option to enter
+	  locations.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RangeState.java:
+	  Improved this state to be the base state for calculation ranges.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: This
+	  state no longer inherits from RangeState which now is used as base
+	  state for kilometer ranges.
+
+	* 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
+	  the DistanceSelect state.
+
+2011-06-28  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* pom.xml: Downgraded Trove to 1.1-beta-5, because the new
+	  later ones are removed from the maven repos.
+
+	  The functionality we need is in 1.1 so this downgrade should
+	  cause no problems.
+
+	  Would be nice if we would support the maintainers of trove to 
+	  bring there new versions back into the main maven repos.
+
+2011-06-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: Repaired the JBoss repository which place has changed.
+
+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:51 2012 +0200
@@ -0,0 +1,310 @@
+2011-09-19  RELEASE 2.5
+
+    NEW:
+
+        * Introduced the concept of a "datacage": the datacage is a service that
+          provides a list of chart themes that fit to a given chart type. The
+          service accepts a user uuid, the uuid of a master Artifact, the name
+          of the output type and a set of further string parameters. In general,
+          we distinguish between user-specific datacage and system-specific
+          datacage:
+          The user-specific datacage returns themes provided by old
+          calculations computed by the user.
+          The system-specific datacage returns themes that might be generated
+          using the data stored in the flys-backend.
+
+        * Introduced a database used by the datacage that stores information
+          about Artifacts, their outputs and their facets. This database
+          requires synchronization with the Artifact database. The intent of
+          this database is to have a fast access to data stored in Artifacts.
+
+        * Introduced a mechanism to clone existing Artifacts (with or without
+          restrictions). For cloning an Artifact, it is necessary to specify the
+          UUID of a "model Artifact". The clone will be based on that model by
+          extracting all required data from it.
+
+        * Introduced "recommendations": recommendations are themes in charts or
+          maps (Artifacts/Facets) that should be added automatically to an
+          existing chart/map. They are provided by the datacage.
+
+        * Introduced new Artifact types to provide further chart themes:
+          - Annotations
+          - Mainvalues
+
+        * Introduced new Facet type WMSLayerFacet. This type stores all required
+          information (server url, layer names, layer extent, layer srid) to
+          display a WMS layer in a WMS client.
+
+        * Introduced a new chart output "Querprofildiagramm". This output
+          calculates a waterlevel and displays it as single line together with
+          one or more cross section lines ("Querprofilspuren").
+
+        * Introduced a new chart output "W-Differenzen". This output calculates
+          waterlevel differences based on at least two waterlevels (a
+          "W-Differenzen" output can consist of more than a single waterlevel
+          difference calculation). Each waterlevel difference calculation
+          results in three chart themes: two W curves and a W-Differences curve.
+
+        * Introduced a new output "Ãœberschwemmungskarte". The visualization of
+          this output is a map. One of the map's layers is a WMS layer that
+          displays the calculation result of WSPLGEN (external C++ tool).
+
+        * Improved the rendering process of chart themes: the styles configured
+          for themes are now used.
+
+        * Improved the DistanceInfoService: it supports filters to filter the
+          type of items returned by this service (reduces the number of returned
+          items of course).
+
+        * Introduced a CSV export for "W-Differenzen".
+
+        * Downgraded GNU Trove to 1.1-beta-5 (later versions have been removed
+          from maven repositories).
+
+
+    FIXES:
+
+        * flys/issue135 (Diagramm: Trotz abgeschalteter Themen bleiben Beschriftungen bestehen)
+
+        * flys/issue159 (WINFO: Radiobutton - Ortsauswahl bei "W für ungleichwertigen Abflusslängsschnitt" entfernen)
+
+        * flys/issue160 (WINFO: Auswahltabelle Orte bei Modus Strecke nach Step-Back)
+
+        * flys/issue176 (Diagramm: Benennung eines Abflusses bei gewählter Höhe am Pegel)
+
+        * flys/issue180 (WINFO: Zeilen der Tabelle können nach der Markierung in die Zwischenablage kopiert werden.)
+
+        * flys/issue181 (Erstes Thema in der Themenliste wählt sich automatisch wieder an)
+
+        * flys/issue191 (AT-Export: Längsten monoton steigenden Bereich exportieren.)
+
+        * flys/issue219 (W-INFO: Abflusskurvenberechnung / keine Themen im Diagramm)
+
+        * flys/issue254 (Datenkorb: Klonen von Artefakten in anderen Collections + Facettenfilter zum Ausblenden)
+
+        * flys/issue256 (Datenkorb: XXX Issue festhalten)
+
+        * flys/issue258 (Datenkorb: Outs statt States führen)
+
+        * flys/issue259 (Daten aus Datenkorb in Diagramm einladen)
+
+        * flys/issue260 (Datenkorb: Masterartefakt in View aufführen)
+
+        * flys/issue262 (Datenkorb: Vereinigung der beiden Konfigurations-Templates)
+
+        * flys/issue279 (WINFO: Elbe Wasserspiegellage - Index Out of Bounds)
+
+        * flys/issue280 (BoundingBoxen von Streckenfavoriten und Haupt- und Extremwerten unsichtbar machen)
+
+        * flys/issue281 (Karte: Auswahl der berechnten Wasserspiegellage über Inline-Datenkorb)
+
+        * flys/issue282 (Karte: Abstand interpolierte Profile - Default wert)
+
+        * flys/issue290 (Karte: Eingabe von Differenzen zw. WSP und Gelände findet keine Ausprägung in der Karte)
+
+        * flys/issue303 (Keine Streckenfavoriten, wenn nur Q im Längsschnittdiagram ausgewählt)
+
+        * flys/issue309 (Querprofil: Manuelle Eingabe funktioniert nicht nach Return (nur nach Tab))
+
+        * flys/issue310 (Querprofil: Farben der Themen)
+
+        * flys/issue311 (Querprofil: i18n)
+
+
+
+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:51 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:51 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:51 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/annotation.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<artifact name="annotation">
+    <states>
+        <state id="state.annotation.river"
+               description="state.annotation.river"
+               state="de.intevation.flys.artifacts.states.AnnotationRiverState">
+           <outputmodes>
+             <outputmode name="longitudinal_section" description="output.longitudinal_section" mime-type="image/png" type="chart">
+                  <facets>
+                     <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations" />
+                  </facets>
+              </outputmode>
+           </outputmodes>
+        </state>
+    </states>
+</artifact>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/artifacts/riveraxis.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<artifact name="riveraxis">
+    <states>
+        <state id="state.riveraxis.layer"
+               description="state.riveraxis.layer.description"
+               state="de.intevation.flys.artifacts.states.RiverAxisState">
+            <outputmodes>
+                <outputmode name="floodmap" description="output.uesk.map.description" type="map">
+                    <facets>
+                        <facet name="floodmap.riveraxis"/>
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+    </states>
+</artifact>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/artifacts/waterlevel.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<artifact name="waterlevel">
+    <states>
+        <state id="state.waterlevel.done"
+               description="state.waterlevel.done"
+               state="de.intevation.flys.artifacts.states.WaterlevelInfoState">
+           <outputmodes>
+             <outputmode name="w_differences" description="output.w_differences" mime-type="image/png" type="chart">
+                  <facets>
+                     <facet name="longitudinal_section.w" description="facet.longitudinal_section.w" />
+                  </facets>
+              </outputmode>
+           </outputmodes>
+        </state>
+    </states>
+</artifact>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/artifacts/winfo.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,380 @@
+<?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.distance_only"/>
+            <condition data="calculation_mode" value="calc.flood.map" 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.distance"/>
+            <condition data="calculation_mode" value="calc.discharge.longitudinal.section" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.calculation_mode"/>
+            <to state="state.winfo.waterlevel_pair_select"/>
+            <condition data="calculation_mode" value="calc.w.differences" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.waterlevel_pair_select"/>
+            <to state="state.winfo.w_differences"/>
+            <condition data="calculation_mode" value="calc.w.differences" 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" type="chart">
+                    <facets>
+                        <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/>
+                        <facet name="mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/>
+                        <facet name="mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/>
+                    </facets>
+                </outputmode>
+                <!-- TODO: Do we want an error report? -->
+            </outputmodes>
+        </state>
+
+        <state id="state.winfo.distance_only" description="state.winfo.distance_only" state="de.intevation.flys.artifacts.states.DistanceOnlySelect">
+            <data name="ld_from" type="Double" />
+            <data name="ld_to"   type="Double" />
+        </state>
+
+        <state id="state.winfo.distance" description="state.winfo.distance" state="de.intevation.flys.artifacts.states.DistanceSelect">
+            <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" type="chart">
+                    <facets>
+                        <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/>
+                        <facet name="mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/>
+                        <facet name="mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/>
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.distance_only"/>
+            <to state="state.winfo.uesk.wsp"/>
+            <condition data="calculation_mode" value="calc.flood.map" operator="equal"/>
+        </transition>
+
+        <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" type="chart">
+                    <facets>
+                        <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/>
+                        <facet name="mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/>
+                        <facet name="mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/>
+                    </facets>
+                </outputmode>
+                <!-- TODO: Do we want an error report? -->
+            </outputmodes>
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.distance"/>
+            <to state="state.winfo.wq_adapted"/>
+        </transition>
+
+        <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"/>
+            <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.waterlevel_pair_select" description="state.winfo.waterlevel_pair_select" state="de.intevation.flys.artifacts.states.WaterlevelPairSelectState">
+            <data name="diffids" type="String" />
+        </state>
+
+        <state id="state.winfo.w_differences" description="state.winfo.w_differences" state="de.intevation.flys.artifacts.states.WDifferencesState">
+
+            <outputmodes>
+                <outputmode name="w_differences" description="output.w_differences" mime-type="image/png" type="chart">
+                    <facets>
+                        <facet name="longitudinal_section.w" description="facet.longitudinal_section.w"/>
+                        <facet name="w_differences" description="facet.w_differences"/>
+                        <facet name="other.wkms" description="facet.other.wkms"/>
+                        <facet name="heightmarks_points" description="facet.other.wkms.heightmarks_points"/>
+                        <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="w_differences_export" description="output.w_differences.export" mime-type="text/plain" type="export">
+                    <facets>
+                        <facet name="csv" description="facet.w_differences.csv" />
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </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" type="chart">
+                    <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" type="export">
+                    <facets>
+                        <facet name="csv" description="facet.durationcurve_export.csv" />
+                    </facets>
+                </outputmode>
+                <outputmode name="durationcurve_report" description="output.durationcurve_report" mime-type="text/xml" type="report">
+                    <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" type="chart">
+                    <facets>
+                        <facet name="computed_discharge_curve.q" description="facet.computed_discharge_curve.q"/>
+                        <facet name="computed_discharge_curve.mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/>
+                        <facet name="computed_discharge_curve.mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="computed_dischargecurve_export" description="output.computed_dischargecurve_export" mime-type="text/plain" type="export">
+                    <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" type="report">
+                    <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" type="export">
+                    <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" type="chart">
+                  <facets>
+                    <facet name="longitudinal_section.w" description="facet.longitudinal_section.w"/>
+                    <facet name="longitudinal_section.q" description="facet.longitudinal_section.q"/>
+                    <facet name="w_differences"          description="facet.w_differences"/>
+                    <facet name="other.wkms"             description="facet.other.wkms"/>
+                    <facet name="heightmarks_points" description="facet.other.wkms.heightmarks_points"/>
+                    <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations"/>
+                  </facets>
+                </outputmode>
+                <outputmode name="waterlevel_export" description="output.waterlevel_export" mime-type="text/plain" type="export">
+                  <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" type="report">
+                  <facets>
+                    <facet name="report" description="facet.waterlevel_export.report"/>
+                  </facets>
+                </outputmode>
+                <outputmode name="cross_section" description="output.cross_section" mime-type="image/png" type="chart">
+                  <facets>
+                    <facet name="cross_section_water_line" description="facet.cross_section_water_line"/>
+                    <facet name="cross_section"            description="facet.cross_section"/>
+                  </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" type="chart">
+                    <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" type="export">
+                    <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" type="report">
+                    <facets>
+                        <facet name="report" description="facet.discharge_longitudinal_section_export.report"/>
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.waterlevel"/>
+            <to state="state.winfo.uesk.dgm"/>
+        </transition>
+
+        <state id="state.winfo.uesk.wsp" description="state.winfo.uesk.wsp" state="de.intevation.flys.artifacts.states.WaterlevelSelectState">
+            <data name="wsp" type="String" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.uesk.wsp"/>
+            <to state="state.winfo.uesk.dgm"/>
+        </transition>
+
+        <state id="state.winfo.uesk.dgm" description="state.winfo.uesk.dgm" state="de.intevation.flys.artifacts.states.DGMSelect">
+            <data name="dgm" type="String" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.uesk.dgm"/>
+            <to state="state.winfo.uesk.profiles"/>
+        </transition>
+
+        <state id="state.winfo.uesk.profiles" description="state.winfo.uesk.profiles" state="de.intevation.flys.artifacts.states.ProfileDistanceSelect">
+            <data name="profile_distance" type="String" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.uesk.profiles"/>
+            <to state="state.winfo.uesk.floodplain"/>
+        </transition>
+
+        <state id="state.winfo.uesk.floodplain" description="state.winfo.uesk.floodplain.description" state="de.intevation.flys.artifacts.states.FloodplainChoice">
+            <data name="use_floodplain" type="Boolean" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.uesk.floodplain"/>
+            <to state="state.winfo.uesk.differences"/>
+        </transition>
+
+        <state id="state.winfo.uesk.differences" description="state.winfo.uesk.differences" state="de.intevation.flys.artifacts.states.WaterlevelGroundDifferences">
+            <data name="diff_from" type="Double" />
+            <data name="diff_to"   type="Double" />
+            <data name="diff_diff" type="Double" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.uesk.differences"/>
+            <to state="state.winfo.uesk.scenario"/>
+        </transition>
+
+        <state id="state.winfo.uesk.scenario" description="state.winfo.uesk.scenario" state="de.intevation.flys.artifacts.states.ScenarioSelect">
+            <data name="scenario" type="String" />
+            <data name="uesk.barriers" type="String" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.uesk.scenario"/>
+            <to state="state.winfo.uesk.uesk"/>
+        </transition>
+
+        <state id="state.winfo.uesk.uesk" description="state.winfo.uesk.uesk" state="de.intevation.flys.artifacts.states.FloodMapState">
+            <outputmodes>
+                <outputmode name="floodmap" description="output.uesk.map.description" type="map">
+                    <facets>
+                        <facet name="floodmap.wsplgen"/>
+                        <facet name="floodmap.barriers"/>
+                        <facet name="floodmap.riveraxis"/>
+                        <facet name="floodmap.wmsbackground"/>
+                        <facet name="floodmap.kms"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="wsplgen_report" description="output.wsplgen_report" mime-type="text/xml" type="report">
+                    <facets>
+                        <facet name="report" description="facet.wsplgen_export.report"/>
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+    </states>
+</artifact>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/artifacts/wmsbackground.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<artifact name="wmsbackground">
+    <states>
+        <state id="state.wmsbackground.layer"
+               description="state.wmsbackground.layer.description"
+               state="de.intevation.flys.artifacts.states.WMSBackgroundState">
+            <outputmodes>
+                <outputmode name="floodmap" description="output.uesk.map.description" type="map">
+                    <facets>
+                        <facet name="floodmap.wmsbackground"/>
+                    </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:51 2012 +0200
@@ -0,0 +1,84 @@
+<?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 as Documents -->
+    <cache name="service-distanceinfo"
+           maxElementsInMemory="20"
+           eternal="false"
+           timeToIdleSeconds="360"
+           timeToLiveSeconds="86400"
+           memoryStoreEvictionPolicy="LFU"
+           />
+
+    <!-- This one is used to cache the distance infos per river as Lists -->
+    <cache name="annotations"
+           maxElementsInMemory="2000"
+           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"
+           />
+
+    <!-- This one is used to cache the non-computed wst-values.-->
+    <cache name="wst-value-table-static"
+           maxElementsInMemory="200"
+           eternal="false"
+           timeToLiveSeconds="172800"
+           overflowToDisk="true"
+           diskPersistent="true"
+           memoryStoreEvictionPolicy="LRU"
+           />
+
+    <!-- This one is used for the SQL statements used by the static datacage -->
+    <cache name="datacage.db"
+           maxElementsInMemory="2000"
+           eternal="false"
+           timeToLiveSeconds="7200"
+           memoryStoreEvictionPolicy="LFU"
+           />
+
+    <!-- This one is used for the cross section next neighbor lookup -->
+    <cache name="cross-section-kms"
+           maxElementsInMemory="50"
+           eternal="false"
+           timeToLiveSeconds="7200"
+           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:51 2012 +0200
@@ -0,0 +1,218 @@
+<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. -->
+            <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-factory name="riveraxis" description="Factory to create an artifact to be used in WINFO"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.RiverAxisArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory name="wmskmfactory" description="Factory to create an artifact that generates WMS facets for KMs."
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.WMSKmArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory name="wmsbackground" description="Factory to create an artifact to be used in WINFO"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.WMSBackgroundArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory name="annotations" description="Factory to create an artifact to access Annotations for Points at rivers"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.AnnotationArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory name="waterlevel" description="Factory to create an artifact to access waterlevel data"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.WaterlevelArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory name="mainvalue" description="Factory to create an artifact to access Main Values for discharge curve diagrams"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.MainValuesArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory name="staticwkms" description="Factory to create an artifact to access 'other' WKms data"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.StaticWKmsArtifact">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-factory
+                name="mapinfo"
+                service="de.intevation.flys.artifacts.services.MapInfoService"
+                description="The service provides some basic information to create a WMS for a specific river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+            <service-factory
+                name="cross-section-km"
+                service="de.intevation.flys.artifacts.services.CrossSectionKMService"
+                description="The service provides the N next neighbored kms and ids of cross section lines for given cross section id, km and N.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+        </service-factories>
+
+    </factories>
+
+    <lifetime-listeners>
+        <listener>de.intevation.flys.artifacts.datacage.Datacage</listener>
+    </lifetime-listeners>
+
+    <backend-listeners>
+        <listener>de.intevation.flys.artifacts.datacage.DatacageBackendListener</listener>
+    </backend-listeners>
+
+    <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" />
+        <artifact name="waterlevel" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/waterlevel.xml" />
+        <artifact name="riveraxis" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/riveraxis.xml" />
+        <artifact name="annotation" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/annotation.xml" />
+        <artifact name="wmsbackground" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/wmsbackground.xml" />
+    </artifacts>
+
+
+    <hooks>
+        <hook
+            class="de.intevation.flys.artifacts.CollectionMonitor"
+            applies="post-feed,post-advance"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+            xlink:href="${artifacts.config.dir}/output-defaults.xml">
+        </hook>
+    </hooks>
+
+    <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="cross_section">de.intevation.flys.exports.CrossSectionGenerator</output-generator>
+        <output-generator name="cross_section_chartinfo">de.intevation.flys.exports.CrossSectionInfoGenerator</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>
+        <output-generator name="w_differences">de.intevation.flys.exports.WDifferencesCurveGenerator</output-generator>
+        <output-generator name="w_differences_chartinfo">de.intevation.flys.exports.WDifferencesCurveInfoGenerator</output-generator>
+        <output-generator name="w_differences_export">de.intevation.flys.exports.WDifferencesExporter</output-generator>
+        <output-generator name="floodmap">de.intevation.flys.exports.MapGenerator</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>
+        <output-generator name="wsplgen_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>
+
+    <!-- Path to the template file of the meta data. -->
+    <metadata>
+        <template>${artifacts.config.dir}/meta-data.xml</template>
+    </metadata>
+
+    <!-- The floodmap configuration for each supported river. Each element
+         requires a srid, wms and background-wms. Those information are used for
+         creating a Map view.-->
+    <floodmap>
+        <shapefile-path value="${artifacts.config.dir}/../shapefiles"/>
+        <mapserver>
+            <server path="http://flys-devel.intevation.de/cgi-bin/"/>
+            <mapfile path="${artifacts.config.dir}/../flys.map"/>
+            <templates path="${artifacts.config.dir}/mapserver/"/>
+            <map-template path="mapfile.vm"/>
+        </mapserver>
+
+        <velocity>
+            <logfile path="${artifacts.config.dir}/../velocity_log.log"/>
+        </velocity>
+
+        <river name="Saar">
+            <srid value="31466"/>
+            <river-wms url="http://flys-devel.intevation.de/cgi-bin/saar-wms"/>
+            <background-wms url="http://vmap0.tiles.osgeo.org/wms/vmap0" layers="basic"/>
+        </river>
+        <river name="Mosel">
+            <srid value="31466"/>
+            <river-wms url="http://flys-devel.intevation.de/cgi-bin/mosel-wms"/>
+            <background-wms url="http://vmap0.tiles.osgeo.org/wms/vmap0" layers="basic"/>
+        </river>
+        <river name="Elbe">
+            <srid value="31466"/>
+            <river-wms url="http://flys-devel.intevation.de/cgi-bin/elbe-wms"/>
+            <background-wms url="http://vmap0.tiles.osgeo.org/wms/vmap0" layers="basic"/>
+        </river>
+    </floodmap>
+
+    <rest-server>
+        <!--  The port which the ArtifactDatabase (ArtifactServer) 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>
+
+    <!-- This is the default configuration of the datacage db:
+    <datacage>
+        <user/>
+        <password/>
+        <driver>org.h2.Driver</driver>
+        <url>jdbc:h2:mem:datacage;INIT=RUNSCRIPT FROM '${artifacts.config.dir}/datacage.sql'</url>
+    </datacage>
+    -->
+
+    <database>
+        <!-- This Section configures the Settings for connecting to the 
+             Artifact-Database instance. e.g. SQLite -->
+        <user>SA</user>
+        <password></password>
+        <!-- For use with a postgresql database use the appropriate driver-->
+        <!--driver>org.postgresql.Driver</driver-->
+        <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/datacage.sql	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,104 @@
+BEGIN;
+
+CREATE SEQUENCE USERS_ID_SEQ;
+
+CREATE TABLE users (
+    id  INT  PRIMARY KEY NOT NULL,
+    gid UUID             NOT NULL UNIQUE
+);
+
+CREATE SEQUENCE COLLECTIONS_ID_SEQ;
+
+CREATE TABLE collections (
+    id       INT  PRIMARY KEY NOT NULL,
+    gid      UUID             NOT NULL UNIQUE,
+    user_id  INT              NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    name     VARCHAR(256)     NOT NULL,
+    creation TIMESTAMP        NOT NULL
+);
+
+CREATE SEQUENCE ARTIFACTS_ID_SEQ;
+
+CREATE TABLE artifacts (
+    id       INT  PRIMARY KEY NOT NULL,
+    gid      UUID             NOT NULL UNIQUE,
+    state    VARCHAR(256)     NOT NULL,
+    creation TIMESTAMP        NOT NULL
+);
+
+CREATE SEQUENCE COLLECTION_ITEMS_ID_SEQ;
+
+CREATE TABLE collection_items (
+    id            INT PRIMARY KEY NOT NULL,
+    collection_id INT             NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
+    artifact_id   INT             NOT NULL REFERENCES artifacts(id)   ON DELETE CASCADE
+);
+
+CREATE SEQUENCE ARTIFACT_DATA_ID_SEQ;
+
+CREATE TABLE artifact_data (
+    id          INT PRIMARY KEY NOT NULL,
+    artifact_id INT             NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE,
+    kind        VARCHAR(256)    NOT NULL,
+    k           VARCHAR(256)    NOT NULL,
+    v           VARCHAR(256),   -- Maybe too short
+    UNIQUE (artifact_id, k)
+);
+
+CREATE SEQUENCE OUTS_ID_SEQ;
+
+CREATE TABLE outs (
+    id          INT PRIMARY KEY NOT NULL,
+    artifact_id INT             NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE,
+    name        VARCHAR(256)    NOT NULL,
+    description VARCHAR(256),
+    out_type    VARCHAR(256)
+);
+
+CREATE SEQUENCE FACETS_ID_SEQ;
+
+CREATE TABLE facets (
+    id          INT PRIMARY KEY NOT NULL,
+    out_id      INT             NOT NULL REFERENCES outs(id) ON DELETE CASCADE,
+    name        VARCHAR(256)    NOT NULL,
+    num         INT             NOT NULL,
+    state       VARCHAR(256)    NOT NULL,
+    description VARCHAR(256),
+    UNIQUE (out_id, num, name)
+);
+
+CREATE VIEW master_artifacts AS
+    SELECT a2.id             AS id,
+           a2.gid            AS gid,
+           a2.state          AS state,
+           a2.creation       AS creation,
+           ci2.collection_id AS collection_id
+    FROM   collection_items ci2 
+           JOIN artifacts a2 
+             ON ci2.artifact_id = a2.id 
+           JOIN (SELECT ci.collection_id AS c_id, 
+                        MIN(a.creation)  AS oldest_a 
+                 FROM   collection_items ci 
+                        JOIN artifacts a 
+                          ON ci.artifact_id = a.id 
+                 GROUP  BY ci.collection_id) o 
+             ON o.c_id = ci2.collection_id 
+    WHERE  a2.creation = o.oldest_a;
+
+-- DROP VIEW master_artifacts;
+-- DROP SEQUENCE USERS_ID_SEQ;
+-- DROP SEQUENCE COLLECTIONS_ID_SEQ;
+-- DROP SEQUENCE ARTIFACTS_ID_SEQ;
+-- DROP SEQUENCE COLLECTION_ITEMS_ID_SEQ;
+-- DROP SEQUENCE ARTIFACT_DATA_ID_SEQ;
+-- DROP SEQUENCE OUTS_ID_SEQ;
+-- DROP SEQUENCE FACETS_ID_SEQ;
+-- DROP TABLE facets;
+-- DROP TABLE outs;
+-- DROP TABLE artifact_data;
+-- DROP TABLE collection_items;
+-- DROP TABLE collections;
+-- DROP TABLE artifacts;
+-- DROP TABLE users;
+
+COMMIT;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/barrier_lines_class.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,32 @@
+CLASS
+    NAME "Damm"
+    EXPRESSION ("[TYP]"="Damm")
+    STYLE
+        SIZE 5
+        OUTLINECOLOR "#008000"
+    END
+END
+CLASS
+    NAME "Rohr 1"
+    EXPRESSION ("[TYP]"="Rohr 1")
+    STYLE
+        SIZE 5
+        OUTLINECOLOR "#800080"
+    END
+END
+CLASS
+    NAME "Rohr 2"
+    EXPRESSION ("[TYP]"="Rohr 2")
+    STYLE
+        SIZE 5
+        OUTLINECOLOR "#808080"
+    END
+END
+CLASS
+    NAME "Graben"
+    EXPRESSION ("[TYP]"="Graben")
+    STYLE
+        SIZE 5
+        OUTLINECOLOR "#800000"
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/barrier_polygons_class.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,7 @@
+CLASS
+    NAME "POLYGON_BARRIERS"
+    STYLE
+        SIZE 5
+        OUTLINECOLOR "#FF8000"
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/db_layer.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,37 @@
+LAYER
+    NAME "$LAYER.getName()"
+    TYPE $LAYER.getType()
+
+    INCLUDE "$CONFIGDIR/mapserver/dbconnection.include"
+    DATA    "$LAYER.getData()"
+    FILTER  '$LAYER.getFilter()'
+    EXTENT  $LAYER.getExtent()
+
+    STATUS    ON
+    TEMPLATE  map.html
+    TOLERANCE 10
+    DUMP      TRUE
+    #if( $LAYER.getGroup() )
+        GROUP "$LAYER.getGroup()"
+    #end
+
+    METADATA
+        "wms_title" "$LAYER.getTitle()"
+        "gml_include_items" "all"
+        #if ( $LAYER.getGroupTitle() )
+            "wms_group_title" "$LAYER.getGroupTitle()"
+        #end
+    END
+
+    #if ( $LAYER.getStyle() )
+        $LAYER.getStyle()
+    #else
+        CLASS
+            NAME ""
+            STYLE
+                SIZE 5
+                OUTLINECOLOR "#000000"
+            END
+        END
+    #end
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/dbconnection.include	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,2 @@
+CONNECTIONTYPE postgis
+CONNECTION "dbname='flys3' host='czech-republic.atlas.intevation.de' port=5432 user='flys' password='flys' sslmode=disable"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/fontset.txt	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,1 @@
+FreeSans /usr/share/fonts/truetype/freefont/FreeSans.ttf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/layer.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,34 @@
+LAYER
+    NAME      "$LAYER.getName()"
+    TYPE      $LAYER.getType()
+    DATA      "$LAYER.getDirectory()/$LAYER.getData()"
+    STATUS    ON
+    TEMPLATE  map.html
+    TOLERANCE 10
+    DUMP      TRUE
+    #if( $LAYER.getGroup() )
+        GROUP "$LAYER.getGroup()"
+    #end
+
+    METADATA
+        "wms_title" "$LAYER.getTitle()"
+        "gml_include_items" "all"
+        #if ( $LAYER.getGroupTitle() )
+            "wms_group_title" "$LAYER.getGroupTitle()"
+        #end
+    END
+
+    #if ( !$LAYER.getStyle() )
+        #if ( $LAYER.getGroupTitle() )
+            #if ( $LAYER.getType() == "POLYGON" )
+                #include("barrier_polygons_class.vm")
+            #else
+                #include("barrier_lines_class.vm")
+            #end
+        #else
+            #include("wsplgen_class.vm")
+        #end
+    #else
+        $LAYER.getStyle()
+    #end
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/mapfile.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,60 @@
+MAP
+    NAME "FLYS-Map"
+    STATUS ON
+    SIZE 600 400
+    MAXSIZE 4000
+    EXTENT -90 -180 90 180
+    UNITS DD
+    SHAPEPATH "$SHAPEFILEPATH"
+    FONTSET "$CONFIGDIR/mapserver/fontset.txt"
+    SYMBOLSET "$CONFIGDIR/mapserver/symbols.sym"
+    IMAGECOLOR 255 255 255
+    PROJECTION
+        "init=epsg:31466"
+    END
+
+    DEBUG 5
+    CONFIG "MS_ERRORFILE" "/tmp/flys-user-wms.log"
+
+    WEB
+      METADATA
+        "wms_title"             "FLYS Web Map Service"
+        "wms_onlineresource"    "$MAPSERVERURL"
+        "wms_accessconstraints" "none"
+        "wms_fees"              "none"
+        "wms_addresstype"       "postal"
+        "wms_address"           "Any Street"
+        "wms_city"              "Any City"
+        "wms_stateorprovince"   "Any state"
+        "wms_postcode"          "My Postalcode"
+        "wms_country"           "Any Country"
+        "wms_contactperson"     "Any Person"
+        "wms_contactorganization" "Any Orga"
+        "wms_contactelectronicmailaddress" "any-email@example.com"
+        "wms_contactvoicetelephone" "Any's telephone number"
+        "wms_srs" "EPSG:4326 EPSG:31466 EPSG:31467"
+        "wms_feature_info_mime_type" "text/html"
+        "ows_enable_request"   "*"
+      END
+    END
+
+    LEGEND
+        KEYSIZE 20 20
+        STATUS ON
+        TRANSPARENT ON
+
+        LABEL
+            COLOR 150 150 150
+            OUTLINECOLOR 255 255 255
+            TYPE truetype
+            FONT "FreeSans"
+            SIZE 12
+            POSITION AUTO
+        END
+    END
+
+    ## Don't change the following lines.
+    #foreach ($LAYER in $LAYERS)
+        include "$LAYER"
+    #end
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/shapefile_layer.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,34 @@
+LAYER
+    NAME      "$LAYER.getName()"
+    TYPE      $LAYER.getType()
+    DATA      "$LAYER.getDirectory()/$LAYER.getData()"
+    STATUS    ON
+    TEMPLATE  map.html
+    TOLERANCE 10
+    DUMP      TRUE
+    #if( $LAYER.getGroup() )
+        GROUP "$LAYER.getGroup()"
+    #end
+
+    METADATA
+        "wms_title" "$LAYER.getTitle()"
+        "gml_include_items" "all"
+        #if ( $LAYER.getGroupTitle() )
+            "wms_group_title" "$LAYER.getGroupTitle()"
+        #end
+    END
+
+    #if ( !$LAYER.getStyle() )
+        #if ( $LAYER.getGroupTitle() )
+            #if ( $LAYER.getType() == "POLYGON" )
+                #include("barrier_polygons_class.vm")
+            #else
+                #include("barrier_lines_class.vm")
+            #end
+        #else
+            #include("wsplgen_class.vm")
+        #end
+    #else
+        $LAYER.getStyle()
+    #end
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/symbols.sym	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,22 @@
+SYMBOLSET
+SYMBOL
+  NAME 'point'
+  TYPE ELLIPSE
+  POINTS
+    1 1
+  END
+  FILLED TRUE
+END
+SYMBOL
+  NAME "square"
+  TYPE VECTOR
+  POINTS
+    0 0
+    0 1
+    1 1
+    1 0
+    0 0
+  END
+  FILLED TRUE
+END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/wsplgen_class.vm	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,44 @@
+CLASS
+    NAME "0.0 <= DIFF < 1"
+    EXPRESSION ([DIFF] < 1)
+    STYLE
+        SIZE 5
+        COLOR "#B2C9D7"
+    END
+END
+
+CLASS
+    NAME "1.0 <= DIFF < 2"
+    EXPRESSION ([DIFF] >= 1 AND [DIFF] < 2)
+    STYLE
+        SIZE 5
+        COLOR "#6F93AA"
+    END
+END
+
+CLASS
+    NAME "2.0 <= DIFF < 3"
+    EXPRESSION ([DIFF] >= 2 AND [DIFF] < 3)
+    STYLE
+        SIZE 5
+        COLOR "#426F8B"
+    END
+END
+
+CLASS
+    NAME "3.0 <= DIFF < 4"
+    EXPRESSION ([DIFF] >= 3 AND [DIFF] < 4)
+    STYLE
+        SIZE 5
+        COLOR "#214F6C"
+    END
+END
+
+CLASS
+    NAME "Sonstiges"
+    EXPRESSION ([DIFF] >= 4)
+    STYLE
+        SIZE 5
+        COLOR "#021B2A"
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/meta-data.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,701 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage">
+<datacage>
+  <dc:macro name="load-system">
+    <dc:context connection="system">
+      <dc:statement>
+        SELECT id AS river_id, name as river_name FROM rivers
+        WHERE lower(name) LIKE lower(${river})
+      </dc:statement>
+      <dc:elements>
+        <dc:comment>
+          Base-data macros (mostly data imported from wst-files)
+        </dc:comment>
+        <dc:macro name="basedata_0">
+            <dc:comment comment=" BASEDATA ---------------------------"/>
+            <basedata>
+              <dc:context>
+                <dc:statement>
+                  SELECT id          AS prot_id,
+                         description AS prot_description
+                  FROM wsts WHERE kind = 0 AND river_id = ${river_id}
+                </dc:statement>
+                <dc:elements>
+                  <basedata>
+                    <dc:attribute name="name" value="${prot_description}"/>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id       AS prot_column_id,
+                               name     AS prot_column_name,
+                               position AS prot_rel_pos
+                        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="ids" value="base_data-wstv-${prot_rel_pos}-${prot_id}"/>
+                          <dc:attribute name="factory" value="staticwkms"/>
+                        </column>
+                      </dc:elements>
+                    </dc:context>
+                  </basedata>
+                </dc:elements>
+              </dc:context>
+            </basedata>
+        </dc:macro>
+        <dc:macro name="basedata_1_additionals">
+            <dc:comment comment=".ZUS -------------------------------"/>
+            <addtionals>
+              <dc:context>
+                <dc:statement>
+                  SELECT id          AS prot_id,
+                         description AS prot_description
+                  FROM wsts WHERE kind = 1 AND river_id = ${river_id}
+                </dc:statement>
+                <dc:elements>
+                  <additional>
+                    <dc:attribute name="name" value="${prot_description}"/>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id       AS prot_column_id,
+                               name     AS prot_column_name,
+                               position AS prot_rel_pos
+                        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="ids" value="additionals-wstv-${prot_rel_pos}-${prot_id}"/>
+                          <dc:attribute name="factory" value="staticwkms"/>
+                        </column>
+                      </dc:elements>
+                    </dc:context>
+                  </additional>
+                </dc:elements>
+              </dc:context>
+            </addtionals>
+        </dc:macro>
+        <river>
+          <dc:attribute name="name" value="${river_name}"/>
+          <dc:if test="dc:contains($artifact-outs, 'w_differences')">
+            <annotation>
+              <dc:attribute name="factory" value="annotations"/>
+              <dc:attribute name="ids"     value="${river_id}"/>
+            </annotation>
+          </dc:if>
+          <dc:if test="dc:contains($artifact-outs, 'longitudinal_section') or (dc:contains($artifact-outs, 'w_differences'))">
+            <dc:choose>
+              <dc:when test="dc:contains($parameters, 'recommended')">
+              </dc:when>
+              <dc:otherwise>
+                <dc:call-macro name="basedata_0"/>
+                <dc:call-macro name="basedata_1_additionals"/>
+                <dc:comment comment=" FIXATIONS ---------------------------"/>
+                <fixations>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS prot_id,
+                             description AS prot_description
+                      FROM wsts WHERE kind = 2 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <fixation>
+                        <dc:attribute name="name" value="${prot_description}"/>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id       AS prot_column_id,
+                                   name     AS prot_column_name,
+                                   position AS prot_rel_pos
+                            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="ids" value="fixations-wstv-${prot_rel_pos}-${prot_id}"/>
+                              <dc:attribute name="factory" value="staticwkms"/>
+                            </column>
+                          </dc:elements>
+                        </dc:context>
+                      </fixation>
+                    </dc:elements>
+                  </dc:context>
+                </fixations>
+                <dc:comment comment=" HOEHENMARKEN ---------------------------"/>
+                <heightmarks>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS prot_id,
+                             description AS prot_description
+                      FROM wsts WHERE kind = 4 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <heightmark>
+                        <dc:attribute name="name" value="${prot_description}"/>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id       AS prot_column_id,
+                                   name     AS prot_column_name,
+                                   position AS prot_rel_pos
+                            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="ids" value="heightmarks_points-wstv-${prot_rel_pos}-${prot_id}"/>
+                              <dc:attribute name="factory" value="staticwkms"/>
+                            </column>
+                          </dc:elements>
+                        </dc:context>
+                      </heightmark>
+                    </dc:elements>
+                  </dc:context>
+                </heightmarks>
+               </dc:otherwise>
+            </dc:choose>
+          </dc:if>
+
+          <dc:if test="dc:contains($artifact-outs, 'waterlevels')">
+            <flood-protections>
+              <dc:attribute name="id" value="flood-protections-${river_id}"/>
+              <dc:context>
+                <dc:statement>
+                  SELECT id          AS prot_id,
+                         description AS prot_description
+                  FROM wsts WHERE kind = 5 AND river_id = ${river_id}
+                </dc:statement>
+                <dc:elements>
+                  <flood-protection>
+                    <dc:attribute name="name" value="${prot_description}"/>
+                    <dc:attribute name="db-id" value="${prot_id}"/>
+                    <dc:attribute name="factory" value="staticwkms"/>
+                    <columns>
+                      <dc:context>
+                        <dc:statement>
+                          SELECT id       AS prot_column_id,
+                                 name     AS prot_column_name,
+                                 position AS prot_rel_pos
+                          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="ids" value="flood_protection-wstv-${prot_rel_pos}-${prot_id}"/>
+                            <dc:attribute name="factory" value="staticwkms"/>
+                          </column>
+                        </dc:elements>
+                      </dc:context>
+                    </columns>
+                  </flood-protection>
+                </dc:elements>
+              </dc:context>
+            </flood-protections>
+            <extra-longitudinal-sections>
+              <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/>
+              <dc:context>
+                <dc:statement>
+                  SELECT id          AS els_id,
+                         description AS els_description
+                  FROM wsts WHERE kind = 1 AND river_id = ${river_id}
+                </dc:statement>
+                <dc:elements>
+                  <extra-longitudinal-section>
+                    <dc:attribute name="name" value="${els_description}"/>
+                    <dc:attribute name="db-id" value="${els_id}"/>
+                    <columns>
+                      <dc:context>
+                        <dc:statement>
+                          SELECT id       AS els_column_id,
+                                 name     AS els_column_name,
+                                 position AS els_rel_pos
+                          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="ids" value="flood_protection-wstv-${els_rel_pos}-${els_id}"/>
+                            <dc:attribute name="factory" value="staticwkms"/>
+                          </column>
+                        </dc:elements>
+                      </dc:context>
+                    </columns>
+                  </extra-longitudinal-section>
+                </dc:elements>
+              </dc:context>
+            </extra-longitudinal-sections>
+          </dc:if>
+          <dc:if test="dc:contains($artifact-outs, 'computed_discharge_curve') and not (dc:contains($parameters, 'recommended'))">
+              <discharge-table-nn>
+                <discharge-table-gauge>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id   AS gauge_id,
+                             name AS gauge_name
+                      FROM gauges WHERE river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <gauge>
+                        <dc:attribute name="name" value="${gauge_name}"/>
+                        <dc:attribute name="db-id" value="${gauge_id}"/>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT description   AS gauge_desc, 
+                                   d.id          AS discharge_id,
+                                   ti.start_time AS g_start, 
+                                   ti.stop_time  AS g_stop
+                            FROM discharge_tables d JOIN time_intervals ti 
+                            ON d.time_interval_id = ti.id
+                            WHERE d.gauge_id = ${gauge_id} AND d.kind = 1
+                          </dc:statement>
+                          <dc:elements>
+                            <historical>
+                              <dc:attribute name="name" value="${gauge_desc}"/>
+                              <dc:attribute name="from" value="${g_start}"/>
+                              <dc:attribute name="to" value="${g_stop}"/>
+                              <dc:attribute name="db-id" value="${discharge_id}"/></historical>
+                          </dc:elements>
+                        </dc:context>
+                      </gauge>
+                    </dc:elements>
+                  </dc:context>
+                </discharge-table-gauge>
+                <fixations>
+                  <dc:attribute name="id" value="fixations-${river_id}"/>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS fix_id,
+                             description AS fix_description
+                      FROM wsts WHERE kind = 2 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <fixation>
+                        <dc:attribute name="name" value="${fix_description}"/>
+                        <dc:attribute name="db-id" value="${fix_id}"/>
+                        <columns>
+                          <dc:context>
+                            <dc:statement>
+                              SELECT id   AS fix_column_id,
+                                     name AS fix_column_name
+                              FROM wst_columns WHERE wst_id = ${fix_id}
+                              ORDER by position
+                            </dc:statement>
+                            <dc:elements>
+                              <column>
+                                <dc:attribute name="name" value="${fix_column_name}"/>
+                                <dc:attribute name="db-id" value="${fix_column_id}"/>
+                              </column>
+                            </dc:elements>
+                          </dc:context>
+                        </columns>
+                      </fixation>
+                    </dc:elements>
+                  </dc:context>
+                </fixations>
+                <flood-protections>
+                  <dc:attribute name="id" value="flood-protections-${river_id}"/>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS prot_id,
+                             description AS prot_description
+                      FROM wsts WHERE kind = 5 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <flood-protection>
+                        <dc:attribute name="name" value="${prot_description}"/>
+                        <dc:attribute name="db-id" value="${prot_id}"/>
+                        <columns>
+                          <dc:context>
+                            <dc:statement>
+                              SELECT id   AS prot_column_id,
+                                     name AS prot_column_name
+                              FROM wst_columns WHERE wst_id = ${prot_id}
+                              ORDER by position
+                            </dc:statement>
+                            <dc:elements>
+                              <column>
+                                <dc:attribute name="name" value="${prot_column_name}"/>
+                                <dc:attribute name="db-id" value="${prot_column_id}"/>
+                                <dc:attribute name="factory" value="staticwkms"/>
+                              </column>
+                            </dc:elements>
+                          </dc:context>
+                        </columns>
+                      </flood-protection>
+                    </dc:elements>
+                  </dc:context>
+                </flood-protections>
+                <flood-water-marks>
+                  <dc:attribute name="id" value="flood-water-marks-${river_id}"/>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS fw_id,
+                             description AS fw_description
+                      FROM wsts WHERE kind = 4 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <flood-water-mark>
+                        <dc:attribute name="name" value="${fw_description}"/>
+                        <dc:attribute name="db-id" value="${fw_id}"/>
+                        <columns>
+                          <dc:context>
+                            <dc:statement>
+                              SELECT id   AS fw_column_id,
+                                     name AS fw_column_name
+                              FROM wst_columns WHERE wst_id = ${fw_id}
+                              ORDER by position
+                            </dc:statement>
+                            <dc:elements>
+                              <column>
+                                <dc:attribute name="name" value="${fw_column_name}"/>
+                                <dc:attribute name="db-id" value="${fw_column_id}"/></column>
+                            </dc:elements>
+                          </dc:context>
+                        </columns>
+                      </flood-water-mark>
+                    </dc:elements>
+                  </dc:context>
+                </flood-water-marks>
+                <water-levels>
+                  <dc:attribute name="id" value="water-levels-${river_id}"/>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS wl_id,
+                             description AS wl_description
+                      FROM wsts WHERE kind = 0 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <water-level>
+                        <dc:attribute name="name" value="${wl_description}"/>
+                        <dc:attribute name="db-id" value="${wl_id}"/>
+                        <columns>
+                          <dc:context>
+                            <dc:statement>
+                              SELECT id   AS wl_column_id,
+                                     name AS wl_column_name
+                              FROM wst_columns WHERE wst_id = ${wl_id}
+                              ORDER by position
+                            </dc:statement>
+                            <dc:elements>
+                              <column>
+                                <dc:attribute name="name" value="${wl_column_name}"/>
+                                <dc:attribute name="db-id" value="${wl_column_id}"/>
+                              </column>
+                            </dc:elements>
+                          </dc:context>
+                        </columns>
+                      </water-level>
+                    </dc:elements>
+                  </dc:context>
+                </water-levels>
+                <extra-longitudinal-sections>
+                  <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id          AS els_id,
+                             description AS els_description
+                      FROM wsts WHERE kind = 1 AND river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                      <extra-longitudinal-section>
+                        <dc:attribute name="name"  value="${els_description}"/>
+                        <dc:attribute name="db-id" value="${els_id}"/>
+                        <columns>
+                          <dc:context>
+                            <dc:statement>
+                              SELECT id   AS els_column_id,
+                                     name AS els_column_name
+                              FROM wst_columns WHERE wst_id = ${els_id}
+                              ORDER by position
+                            </dc:statement>
+                            <dc:elements>
+                              <column>
+                                <dc:attribute name="name"  value="${els_column_name}"/>
+                                <dc:attribute name="db-id" value="${els_column_id}"/>
+                              </column>
+                            </dc:elements>
+                          </dc:context>
+                        </columns>
+                      </extra-longitudinal-section>
+                    </dc:elements>
+                  </dc:context>
+                </extra-longitudinal-sections>
+              </discharge-table-nn>
+          </dc:if>
+          <dc:if test="dc:contains($artifact-outs, 'computed_discharge_curve')">
+              <computed-discharge-curve>
+                <!--mainvalue>
+                  <dc:attribute name="factory" value="mainvalue"/>
+                  <dc:attribute name="db-ids"  value="${river_id}"/>
+                </mainvalue-->
+              </computed-discharge-curve>
+          </dc:if>
+          <dc:if test="dc:contains($artifact-outs, 'longitudinal_section')">
+              <longitudinal-section>
+                <dc:call-macro name="longitudinal_section-recommended"/>
+              </longitudinal-section>
+              <dc:macro name="longitudinal_section-recommended">
+                <annotation>
+                          <dc:attribute name="factory" value="annotations"/>
+                          <dc:attribute name="ids" value="${river_id}"/>
+                </annotation>
+              </dc:macro>
+          </dc:if>
+          <dc:if test="dc:contains($artifact-outs, 'floodmap')">
+              <floodmap>
+              <dc:choose>
+                  <dc:when test="dc:contains($parameters, 'recommended')">
+                    <dc:call-macro name="flood-map-recommended"/>
+                  </dc:when>
+                  <dc:when test="dc:contains($parameters, 'dem')">
+                    <dc:call-macro name="flood-map-dem"/>
+                  </dc:when>
+                  <dc:otherwise>
+                    <dc:call-macro name="flood-map-complete"/>
+                  </dc:otherwise>
+              </dc:choose>
+              </floodmap>
+              <dc:macro name="flood-map-recommended">
+                <dc:comment>
+                   FIXME: Following two macros look identical to me.
+                </dc:comment>
+                  <kilometrage>
+                      <riveraxis>
+                          <dc:attribute name="factory" value="riveraxis"/>
+                          <dc:attribute name="ids" value="${river_id}"/>
+                      </riveraxis>
+                  </kilometrage>
+                  <rastermap>
+                      <background>
+                          <dc:attribute name="factory" value="wmsbackground"/>
+                          <dc:attribute name="ids" value="${river_id}"/>
+                      </background>
+                  </rastermap>
+              </dc:macro>
+              <dc:macro name="flood-map-dem">
+                <dems>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT id    AS dem_id,
+                             lower AS dem_lower,
+                             upper AS dem_upper
+                      FROM dem WHERE river_id = ${river_id}
+                    </dc:statement>
+                    <dc:elements>
+                        <dem>
+                          <dc:attribute name="factory" value="demfactory"/>
+                          <dc:attribute name="ids" value="${dem_id}"/>
+                          <dc:attribute name="description" value="${dem_lower}-${dem_upper}"/>
+                        </dem>
+                    </dc:elements>
+                  </dc:context>
+                </dems>
+              </dc:macro>
+              <dc:macro name="flood-map-km">
+                <dc:context>
+                  <dc:statement>
+                    SELECT count(*) as km_exists
+                    FROM river_axes_km WHERE river_id = ${river_id}
+                  </dc:statement>
+                   <dc:elements>
+                    <dc:if test="$km_exists>0">
+                      <km>
+                        <dc:attribute name="factory" value="wmskmfactory"/>
+                        <dc:attribute name="ids" value="${river_id}"/>
+                      </km>
+                    </dc:if>
+                  </dc:elements>
+                </dc:context>
+              </dc:macro>
+              <dc:macro name="flood-map-complete">
+                  <kilometrage>
+                      <riveraxis>
+                          <dc:attribute name="factory" value="riveraxis"/>
+                          <dc:attribute name="ids" value="${river_id}"/>
+                      </riveraxis>
+                    <dc:call-macro name="flood-map-km"/>
+                  </kilometrage>
+                  <rastermap>
+                      <background>
+                          <dc:attribute name="factory" value="wmsbackground"/>
+                          <dc:attribute name="ids" value="${river_id}"/>
+                      </background>
+                  </rastermap>
+                  <dc:call-macro name="flood-map-dem"/>
+              </dc:macro>
+          </dc:if>
+        </river>
+      </dc:elements>
+    </dc:context>
+  </dc:macro>
+
+  <dc:choose>
+    <dc:comment>
+      User specific part
+      ------------------
+    </dc:comment>
+    <dc:when test="dc:contains($parameters, 'user-id')">
+      <dc:comment>
+        Show diffferences if longitudinal sections are there.
+        -----------------------------------------------------
+      </dc:comment>
+      <dc:choose>
+      <dc:when test="dc:contains($artifact-outs, 'longitudinal_section')">
+        <dc:context>
+        <dc:statement>
+          SELECT a.gid as aid, f.id AS fid, f.name AS facet_name, f.num AS facet_num, f.description as facet_description
+          FROM outs as o, facets as f, artifacts as a
+          WHERE f.name = 'w_differences' and f.out_id = o.id and o.artifact_id = a.id;
+        </dc:statement>
+            <dc:elements>
+             <differences>
+              <dc:element name="${facet_name}">
+                <dc:attribute name="description" value="${facet_description}"/>
+                <dc:attribute name="factory"     value="winfo"/>
+                <dc:attribute name="artifact-id" value="${aid}"/>
+                <dc:attribute name="ids"         value="${aid}"/>
+                <dc:attribute name="out"         value="w_differences"/>
+              </dc:element>
+             </differences>
+            </dc:elements>
+        </dc:context>
+      </dc:when>
+      </dc:choose>
+      <old_calculations>
+        <dc:context connection="user">
+          <dc:statement>SELECT u.id AS user_id, c.id AS collection_id, c.name as collection_name
+                        FROM collections c JOIN users u ON c.user_id = u.id
+                        WHERE u.gid = CAST(${user-id} AS uuid)
+                        ORDER BY c.creation DESC
+          </dc:statement>
+          <dc:elements>
+            <dc:comment><!--
+            <hello_collection><dc:attribute name="description" value="Collection: id=${collection_id} name='${collection_name}'"/></hello_collection>
+            --></dc:comment>
+            <dc:context>
+              <dc:statement>SELECT m.id AS a_id, m.state AS a_state, m.gid AS a_gid, m.creation AS a_creation
+                            FROM   master_artifacts m
+                            WHERE  m.collection_id = ${collection_id} AND m.gid &lt;&gt; CAST(${artifact-id} AS uuid)
+                            AND EXISTS (
+                                SELECT id FROM artifact_data ad WHERE ad.artifact_id = m.id AND k = 'river' AND v = ${river})
+              </dc:statement>
+              <dc:elements>
+              <dc:comment><!--
+                <hello_artifact><dc:attribute name="description" value="Artifact UUID: ${a_gid}"/></hello_artifact>
+              --></dc:comment>
+                <dc:choose>
+
+
+                  <dc:comment>
+                    Import longitudinal sections only if the ${artifact-out} is defined as 'longitudinal_section'
+                    ---------------------------------------------------------------------------------------------
+                  </dc:comment>
+                  <dc:when test="dc:contains($artifact-outs, 'longitudinal_section')">
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id AS out_id
+                        FROM outs
+                        WHERE artifact_id = ${a_id} AND name = 'longitudinal_section'
+                      </dc:statement>
+                      <dc:elements>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT name AS facet_name, num as facet_num, description AS facet_description
+                            FROM facets
+                            WHERE out_id = ${out_id}
+                            ORDER BY num ASC, name DESC
+                          </dc:statement>
+                          <longitudinal_section_columns>
+                              <dc:attribute name="description" value="${river} ${a_creation}"/>
+                              <dc:elements>
+                                <dc:element name="${facet_name}">
+                                  <dc:attribute name="description" value="${facet_description}"/>
+                                  <dc:attribute name="ids" value="${facet_num}"/>
+                                  <dc:attribute name="factory" value="winfo"/>
+                                  <dc:attribute name="artifact-id" value="${a_gid}"/>
+                                  <dc:attribute name="out" value="longitudinal_section"/>
+                                </dc:element>
+                              </dc:elements>
+                          </longitudinal_section_columns>
+                        </dc:context>
+                      </dc:elements>
+                    </dc:context>
+                  </dc:when>
+                  <dc:comment>
+                    Import of longitudinal sections finished
+                  </dc:comment>
+
+
+                  <dc:comment>
+                    Import Waterlevels only if the ${artifact-out} is defined as 'waterlevels'
+                    --------------------------------------------------------------------------
+                  </dc:comment>
+                  <dc:when test="dc:contains($artifact-outs, 'waterlevels')">
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id AS out_id
+                        FROM outs
+                        WHERE artifact_id = ${a_id} AND name = 'longitudinal_section'
+                      </dc:statement>
+                      <dc:elements>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT name AS facet_name, num as facet_num, description AS facet_description
+                            FROM facets
+                            WHERE out_id = ${out_id} and name = 'longitudinal_section.w'
+                            ORDER BY num ASC, name DESC
+                          </dc:statement>
+                          <waterlevels>
+                            <dc:attribute name="description" value="${river} ${a_creation} ${collection_name}"/>
+                            <dc:elements>
+                              <dc:element name="${facet_name}">
+                                <dc:attribute name="description" value="${facet_description}"/>
+                                <dc:attribute name="ids"         value="${facet_num}"/>
+                                <dc:attribute name="factory"     value="winfo"/>
+                                <dc:attribute name="artifact-id" value="${a_gid}"/>
+                                <dc:attribute name="out"         value="longitudinal_section"/>
+                              </dc:element>
+                            </dc:elements>
+                          </waterlevels>
+                        </dc:context>
+                      </dc:elements>
+                    </dc:context>
+                  </dc:when>
+                  <dc:comment>
+                    Import of waterlevels finished
+                  </dc:comment>
+
+                </dc:choose>
+              </dc:elements>
+            </dc:context>
+          </dc:elements>
+        </dc:context>
+      </old_calculations>
+
+
+      <dc:comment>
+        Include System specific part when 'load-system' is in parameters.
+        -----------------------------------------------------------------
+      </dc:comment>
+      <dc:choose>
+        <dc:when test="dc:contains($parameters,'load-system')">
+          <dc:call-macro name="load-system"/>
+        </dc:when>
+      </dc:choose>
+    </dc:when>
+
+
+    <dc:comment>
+      Include System specific part only if no user ID is given.
+      ---------------------------------------------------------
+    </dc:comment>
+    <dc:otherwise>
+        <dc:call-macro name="load-system"/>
+    </dc:otherwise>
+  </dc:choose>
+</datacage>
+</dc:template>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/themes.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,821 @@
+<themes>
+
+    <!-- Concrete themes are following now! -->
+    <theme name="DischargeCurve">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 153"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="2" hints="h"/>
+        </fields>
+    </theme>
+
+    <!--
+        Discharge Longitudinal Section
+    -->
+    <theme name="LongitudinalSectionW">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ1">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ2">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ5">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 153, 51"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ10">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 204, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ20">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="153, 153, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ25">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 51, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ50">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 153, 153"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ100">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 51"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ200">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ500">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ1000">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQRZ">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HSQ">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="253, 153, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_MHQ">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 255, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_MNQ">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 255, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_MQ">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 51, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_NQ">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="153, 204, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQExtrem">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionPoints">
+        <inherits>
+            <inherit from="LongitudinalSectionW"/>
+        </inherits>
+        <fields>
+            <field name="showlines"  type="boolean" display="Linie anzeigen"  default="false"/>
+            <field name="showpoints" type="boolean" display="Punkte anzeigen" default="true"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ1_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ2_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ5_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 153, 51"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ10_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 204, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ20_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="153, 153, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ25_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 51, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ50_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 153, 153"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ100_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 51"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ200_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ500_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQ1000_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQRZ_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HSQ_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="253, 153, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_MHQ_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 255, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_MNQ_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 255, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_MQ_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 51, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_NQ_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="153, 204, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionW_HQExtrem_Points">
+        <inherits><inherit from="LongitudinalSectionPoints"/></inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+    <!--
+      Longitudinal Section Q's
+    -->
+
+    <theme name="LongitudinalSectionQ">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ1">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ2">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ5">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 153, 51"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ10">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 204, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ20">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="153, 153, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ25">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 51, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ50">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 153, 153"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ100">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 51"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ200">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ500">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQ1000">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQRZ">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HSQ">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="253, 153, 0"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_MHQ">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="102, 255, 102"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_MNQ">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 255, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_MQ">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 51, 204"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_NQ">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="153, 204, 255"/>
+        </fields>
+    </theme>
+
+    <theme name="LongitudinalSectionQ_HQExtrem">
+        <inherits>
+            <inherit from="LongitudinalSectionQ"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+
+    <!--
+        Computed Discharge Curves
+    -->
+    <theme name="ComputedDischargeCurve">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 153"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="2" hints="h"/>
+        </fields>
+    </theme>
+
+    <theme name="ComputedDischargeCurveQ">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+            <inherit from="Text"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="200, 0, 15"/>
+        </fields>
+    </theme>
+
+    <theme name="ComputedDischargeCurveW">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+            <inherit from="Text"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="0, 215, 0"/>
+        </fields>
+    </theme>
+
+    <!--
+        Cross Sections
+    -->
+    <theme name="CrossSection">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0,0,0"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="1" hints="h"/>
+        </fields>
+    </theme>
+
+    <theme name="CrossSectionWaterLine">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0,0,153"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="1" hints="h"/>
+        </fields>
+    </theme>
+
+
+    <!--
+        Duration Curves
+    -->
+    <theme name="DurationCurveW">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0,51,204"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="2" hints="h"/>
+        </fields>
+    </theme>
+
+    <theme name="DurationCurveQ">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0,204,0"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="2" hints="h"/>
+        </fields>
+    </theme>
+
+    <!-- Differences -->
+    <theme name="Differences">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+        </fields>
+    </theme>
+
+
+    <!-- General -->
+    <theme name="WKms">
+       <inherits><inherit from="HiddenColorLines"/></inherits>
+       <fields>
+         <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+       </fields>
+    </theme>
+
+    <theme name="WQKms">
+       <inherits><inherit from="HiddenColorLines"/></inherits>
+       <fields>
+         <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+       </fields>
+    </theme>
+
+    <!--
+        Discharge Longitudinal Section
+    -->
+    <theme name="DischargeLongitudinalSectionW">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+        </fields>
+    </theme>
+
+
+    <theme name="DischargeLongitudinalSectionC">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0 , 0"/>
+        </fields>
+    </theme>
+
+    <theme name="DischargeLongitudinalSectionQ">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
+        </fields>
+    </theme>
+
+
+    <!-- Annotations -->
+    <theme name="Annotations">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+            <inherit from="Text"/>
+            <inherit from="AnnotationText"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </theme>
+
+    <!-- Height Marks -->
+    <theme name="heightmarks_points">
+        <inherits>
+            <inherit from="Points"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/>
+        </fields>
+    </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="10"/>
+        </fields>
+    </theme>
+
+    <theme name="Points" type="virtual">
+        <fields>
+            <field name="showlines"  type="boolean" display="Linie anzeigen"  default="false"/>
+            <field name="linesize"   type="int"     display="Liniendicke"     default="1"/>
+            <field name="linetype"   type="Dash"    display="Linienart"       default="10"/>
+            <field name="showpoints" type="boolean" display="Punkte anzeigen" default="true"/>
+        </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="10"          hints="h"/>
+            <field name="showpoints" type="boolean" display="Datenpunkte anzeigen" default="false" hints="h"/>
+        </fields>
+    </theme>
+
+    <theme name="Text" type="virtual">
+        <fields>
+            <field name="font"      type="Font"  display="Schriftart"      default="arial"/>
+            <field name="textcolor" type="Color" display="Schriftfarbe"    default="0, 0, 0"/>
+            <field name="textsize"  type="int"   display="Schriftgröße" default="10"/>
+            <field name="textstyle" type="Style" display="Schriftstil"     default="standard"/>
+        </fields>
+    </theme>
+
+    <theme name="AnnotationText" type="virtual">
+        <fields>
+            <field name="backgroundcolor"  type="Color"   display="Texthintergrund"      default="255, 255, 255"/>
+            <field name="textorientation"  type="boolean" display="Textausrichtung"      default="false"/>
+            <field name="showbackground"   type="boolean" display="Hintergrund anzeigen" default="false"/>
+        </fields>
+    </theme>
+
+
+    <!-- MAP relevant themes -->
+    <theme name="RiverAxis">
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 205"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="5" hints="h"/>
+        </fields>
+    </theme>
+
+    <theme name="Kms">
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 0"/>
+            <field name="linesize"  type="int"   display="Liniendicke" default="5" hints="h"/>
+            <field name="symbol"    type="Symbol" display="Symbol"     default="square"/>
+        </fields>
+    </theme>
+
+
+    <!--
+      Mappings are following now. A mapping maps between a name of a facet
+      and a theme.
+      Always the first matching mapping is taken, so consider putting most
+      specific mappings on top of the list.
+    -->
+    <mappings>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ1)(\D.*)*" to="LongitudinalSectionW_HQ1_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ2)(\D.*)*" to="LongitudinalSectionW_HQ2_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ5)(\D.*)*" to="LongitudinalSectionW_HQ5_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ10)(\D.*)*" to="LongitudinalSectionW_HQ10_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ20)(\D.*)*" to="LongitudinalSectionW_HQ20_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ25)(\D.*)*" to="LongitudinalSectionW_HQ25_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ50)(\D.*)*" to="LongitudinalSectionW_HQ50_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ100)(\D.*)*" to="LongitudinalSectionW_HQ100_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ200)(\D.*)*" to="LongitudinalSectionW_HQ200_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ500)(\D.*)*" to="LongitudinalSectionW_HQ500_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ1000)(\D.*)*" to="LongitudinalSectionW_HQ1000_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQExtrem)(\D.*)*" to="LongitudinalSectionW_HQExtrem_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQRZ)(\D.*)*" to="LongitudinalSectionW_HQRZ_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HSQ)(\D.*)*" to="LongitudinalSectionW_HSQ_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(MHQ)(\D.*)*" to="LongitudinalSectionW_MHQ_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(MNQ)(\D.*)*" to="LongitudinalSectionW_MNQ_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(MQ)(\D.*)*" to="LongitudinalSectionW_MQ_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(NQ)(\D.*)*" to="LongitudinalSectionW_NQ_Points"/>
+        <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" to="LongitudinalSectionPoints"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ1)(\D.*)*" to="LongitudinalSectionW_HQ1"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ2)(\D.*)*" to="LongitudinalSectionW_HQ2"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ5)(\D.*)*" to="LongitudinalSectionW_HQ5"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ10)(\D.*)*" to="LongitudinalSectionW_HQ10"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ20)(\D.*)*" to="LongitudinalSectionW_HQ20"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ25)(\D.*)*" to="LongitudinalSectionW_HQ25"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ50)(\D.*)*" to="LongitudinalSectionW_HQ50"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ100)(\D.*)*" to="LongitudinalSectionW_HQ100"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ200)(\D.*)*" to="LongitudinalSectionW_HQ200"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ500)(\D.*)*" to="LongitudinalSectionW_HQ500"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQ1000)(\D.*)*" to="LongitudinalSectionW_HQ1000"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQExtrem)(\D.*)*" to="LongitudinalSectionW_HQExtrem"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HQRZ)(\D.*)*" to="LongitudinalSectionW_HQRZ"/>
+        <mapping from="longitudinal_section.w" pattern=".*(HSQ)(\D.*)*" to="LongitudinalSectionW_HSQ"/>
+        <mapping from="longitudinal_section.w" pattern=".*(MHQ)(\D.*)*" to="LongitudinalSectionW_MHQ"/>
+        <mapping from="longitudinal_section.w" pattern=".*(MNQ)(\D.*)*" to="LongitudinalSectionW_MNQ"/>
+        <mapping from="longitudinal_section.w" pattern=".*(MQ)(\D.*)*" to="LongitudinalSectionW_MQ"/>
+        <mapping from="longitudinal_section.w" pattern=".*(NQ)(\D.*)*" to="LongitudinalSectionW_NQ"/>
+        <mapping from="longitudinal_section.w" to="LongitudinalSectionW"/>
+
+        <mapping from="longitudinal_section.q" pattern="(HQ1)(\D.*)*" to="LongitudinalSectionQ_HQ1"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ2)(\D.*)*" to="LongitudinalSectionQ_HQ2"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ5)(\D.*)*" to="LongitudinalSectionQ_HQ5"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ10)(\D.*)*" to="LongitudinalSectionQ_HQ10"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ20)(\D.*)*" to="LongitudinalSectionQ_HQ20"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ25)(\D.*)*" to="LongitudinalSectionQ_HQ25"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ50)(\D.*)*" to="LongitudinalSectionQ_HQ50"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ100)(\D.*)*" to="LongitudinalSectionQ_HQ100"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ200)(\D.*)*" to="LongitudinalSectionQ_HQ200"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ500)(\D.*)*" to="LongitudinalSectionQ_HQ500"/>
+        <mapping from="longitudinal_section.q" pattern="(HQ1000)(\D.*)*" to="LongitudinalSectionQ_HQ1000"/>
+        <mapping from="longitudinal_section.q" pattern="(HQExtrem)(\D.*)*" to="LongitudinalSectionQ_HQExtrem"/>
+        <mapping from="longitudinal_section.q" pattern="(HQRZ)(\D.*)*" to="LongitudinalSectionQ_HQRZ"/>
+        <mapping from="longitudinal_section.q" pattern="(HSQ)(\D.*)*" to="LongitudinalSectionQ_HSQ"/>
+        <mapping from="longitudinal_section.q" pattern="(MHQ)(\D.*)*" to="LongitudinalSectionQ_MHQ"/>
+        <mapping from="longitudinal_section.q" pattern="(MNQ)(\D.*)*" to="LongitudinalSectionQ_MNQ"/>
+        <mapping from="longitudinal_section.q" pattern="(MQ)(\D.*)*" to="LongitudinalSectionQ_MQ"/>
+        <mapping from="longitudinal_section.q" pattern="(NQ)(\D.*)*" to="LongitudinalSectionQ_NQ"/>
+        <mapping from="longitudinal_section.q" to="LongitudinalSectionQ"/>
+
+        <mapping from="discharge_curve.curve" to="DischargeCurve"/>
+        <mapping from="cross_section" to="CrossSection"/>
+        <mapping from="cross_section_water_line" to="CrossSectionWaterLine"/>
+        <mapping from="computed_discharge_curve.q" to="ComputedDischargeCurve"/>
+        <mapping from="duration_curve.w" to="DurationCurveW"/>
+        <mapping from="duration_curve.q" to="DurationCurveQ"/>
+        <mapping from="discharge_longitudinal_section.w" to="DischargeLongitudinalSectionW"/>
+        <mapping from="discharge_longitudinal_section.c" to="DischargeLongitudinalSectionC"/>
+        <mapping from="discharge_longitudinal_section.q" to="DischargeLongitudinalSectionQ"/>
+        <mapping from="computed_discharge_curve.mainvalues.q" to="ComputedDischargeCurveQ"/>
+        <mapping from="computed_discharge_curve.mainvalues.w" to="ComputedDischargeCurveW"/>
+        <mapping from="longitudinal_section.annotations" to="Annotations"/>
+        <mapping from="w_differences" to="Differences"/>
+        <mapping from="floodmap.riveraxis" to="RiverAxis"/>
+        <mapping from="floodmap.kms" to="Kms"/>
+
+        <mapping from="other.wkms" to="WKms"/>
+        <mapping from="other.wqkms" to="WQKms"/>
+        <mapping from="heightmarks_points" to="heightmarks_points"/>
+    </mappings>
+</themes>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/dbconnection.include	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,2 @@
+CONNECTIONTYPE postgis
+CONNECTION "dbname='flys3' host=127.0.0.1 port=5432 user='flys' password='flys' sslmode=disable"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/elbe-mapfile.map	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,73 @@
+MAP
+    NAME "Elbe"
+    STATUS ON
+    SIZE 600 400
+    MAXSIZE 4000
+    EXTENT 3590790.616790 5648985.748203 3868521.325854 5922665.254118
+    UNITS DD
+    #FONTSET "fontset.txt"
+    IMAGECOLOR 255 255 255
+    PROJECTION
+        "init=epsg:31466"
+    END
+
+
+    OUTPUTFORMAT
+        NAME agg
+        DRIVER AGG/PNG
+        IMAGEMODE RGB
+    END
+
+    CONFIG "MS_ERRORFILE" "logs/flys-elbe-wms.log"
+    DEBUG 5
+
+    WEB
+      METADATA
+        "wms_title"             "FLYS-3.0 WMS (ELBE)"
+        "wms_onlineresource"    "http://czech-republic.atlas.intevation.de/cgi-bin/elbe-wms"
+        "wms_accessconstraints" "none"
+        "wms_fees"              "none"
+        "wms_addresstype"       "postal"
+        "wms_address"           "Any Street"
+        "wms_city"              "Any City"
+        "wms_stateorprovince"   "Any state"
+        "wms_postcode"          "My Postalcode"
+        "wms_country"           "Any Country"
+        "wms_contactperson"     "Any Person"
+        "wms_contactorganization" "Any Orga"
+        "wms_contactelectronicmailaddress" "any-email@example.com"
+        "wms_contactvoicetelephone" "Any's telephone number"
+        "wms_srs" "EPSG:31466 EPSG:4326"
+        "wms_feature_info_mime_type" "text/html"
+        "ows_enable_request"   "*"
+      END
+    END
+
+    LAYER
+        NAME riveraxis
+        EXTENT 3590790.616790 5648985.748203 3868521.325854 5922665.254118
+        DEBUG 5
+  
+        METADATA
+            "wms_title" "River Axis"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "dbconnection.include"
+	DATA 'geom FROM "river_axes" USING UNIQUE id USING srid=31466'
+        FILTER "river_id='6'"
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "riveraxis"
+            STYLE
+                SIZE 5
+                COLOR "#000000"
+            END
+        END
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/fontset.txt	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,5 @@
+# LiberationsSans weist eine metrische Identitaet zu Arial auf und ist unter
+# einer freien Lizenz (modifizierte GPL 2) verfuegbar.
+# Quelle: http://de.wikipedia.org/wiki/Arial
+
+LiberationSans-Italic /usr/share/fonts/truetype/LiberationSans-Italic.ttf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/mosel-mapfile.map	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,301 @@
+MAP
+    NAME "Mosel"
+    STATUS ON
+    SIZE 600 400
+    MAXSIZE 4000
+    EXTENT 2457864.326387 5297459.306295 2634771.191263 5586961.449130
+    UNITS DD
+    FONTSET "fontset.txt"
+    SYMBOLSET './symbols/symbols.sym'
+    IMAGECOLOR 255 255 255
+    PROJECTION
+        "init=epsg:31466"
+    END
+
+
+    OUTPUTFORMAT
+        NAME agg
+        DRIVER AGG/PNG
+        IMAGEMODE RGB
+    END
+
+    CONFIG "MS_ERRORFILE" "logs/flys-mosel-wms.log"
+    DEBUG 5
+
+    WEB
+      METADATA
+        "wms_title"             "FLYS-3.0 WMS (SAAR)"
+        "wms_onlineresource"    "http://czech-republic.atlas.intevation.de/cgi-bin/saar-wms"
+        "wms_accessconstraints" "none"
+        "wms_fees"              "none"
+        "wms_addresstype"       "postal"
+        "wms_address"           "Any Street"
+        "wms_city"              "Any City"
+        "wms_stateorprovince"   "Any state"
+        "wms_postcode"          "My Postalcode"
+        "wms_country"           "Any Country"
+        "wms_contactperson"     "Any Person"
+        "wms_contactorganization" "Any Orga"
+        "wms_contactelectronicmailaddress" "any-email@example.com"
+        "wms_contactvoicetelephone" "Any's telephone number"
+        "wms_srs" "EPSG:31466 EPSG:4326"
+        "wms_feature_info_mime_type" "text/html"
+        "ows_enable_request"   "*"
+      END
+    END
+
+    LAYER
+        NAME catchment 
+	EXTENT 2457864.326387 5297459.306295 2634771.191263 5586961.449130
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "catchment"
+        END
+
+        TYPE POLYGON
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM catchment USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "catchment"
+            STYLE
+                COLOR "#000080"
+                OUTLINECOLOR "#000000"
+            END
+        END
+    END
+    LAYER
+        NAME km 
+	GROUP km
+	EXTENT 2525910.000000 5481666.000000 2614362.250000 5582403.000000
+        DEBUG 0
+	DUMP TRUE
+  
+        METADATA
+            "wms_title" "km"
+        END
+
+        TYPE POINT 
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM river_axes_km USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "km"
+  	    STYLE
+             COLOR "#ff0000"
+  	     SYMBOL 'square'
+  	     SIZE 5
+  	   END
+        END
+    END
+
+    LAYER
+	NAME km_annotation 
+	GROUP km
+	EXTENT 2525910.000000 5481666.000000 2614362.250000 5582403.000000
+        DEBUG 5
+	DUMP TRUE
+  
+        METADATA
+            "wms_title" "km_annotation"
+        END
+  	
+	TYPE ANNOTATION 
+  	STATUS ON 
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM river_axes_km USING SRID 31466"
+        FILTER 'river_id=2'
+  	LABELITEM km
+
+        MAXSCALE 25000 
+
+  	CLASS 
+  	  LABEL 
+  	    ANGLE auto 
+  	    SIZE 10
+  	    COLOR "#000000" 
+  	    TYPE truetype 
+  	    FONT LiberationSans-Italic 
+	    POSITION ur
+	    OFFSET 2 2
+  	  END
+  	END 
+    END
+
+    LAYER
+        NAME buildings 
+	Extent 2526389.654387 5492305.031511 2612653.500000 5582427.500000
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "buildings (Bauwerke/Wehre)"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM buildings USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "buildings"
+            STYLE
+        	COLOR "#ff2222"
+            END
+        END
+    END
+    LAYER
+        NAME fixpoints 
+	EXTENT 2525789.640000 5481448.110000 2614324.201153 5582705.474713
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "fixpoints (Geodaesie/Festpunkte)"
+        END
+
+        TYPE POINT 
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM fixpoints USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "fixpoints"
+  	    STYLE
+             COLOR "#ffff00"
+  	     SYMBOL 'square'
+  	     SIZE 6
+  	   END
+        END
+    END
+    LAYER
+        NAME riveraxis
+	EXTENT 2525866.883066 5480800.000000 2614283.382914 5582578.641600
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "River Axes"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM river_axes USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "riveraxes"
+            STYLE
+       		 COLOR "#0000ff"
+            END
+        END
+    END
+    
+    LAYER
+        NAME qps
+	EXTENT 2525526.058176 5481412.836939 2614739.057487 5582746.998120
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "QPS (CrossSectionTracks)"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM cross_section_tracks USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+	MAXSCALEDENOM 100000
+
+        CLASS
+            NAME "qps"
+            STYLE
+        	COLOR "#0000ff"
+            END
+        END
+    END
+
+    LAYER
+        NAME hws 
+	EXTENT 2531846.270698 5501745.060309 2580199.261246 5535383.855597
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "HWS"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM hws USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "hws"
+            STYLE
+        	COLOR "#ff2222" 
+            END
+        END
+    END
+    LAYER
+        NAME floodplain 
+        EXTENT 2583046.060000 5556912.223213 2614325.275661 5582743.883699
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "floodplain (Hydr. Grenzen/Talaue)"
+        END
+
+        TYPE POLYGON
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM floodplain USING SRID 31466"
+        FILTER 'river_id=2'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "floodplain"
+            STYLE
+                COLOR "#800080"
+		OUTLINECOLOR "#000080"
+            END
+        END
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/oracle_dbconnection.include	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,2 @@
+CONNECTIONTYPE oraclespatial
+CONNECTION "flys3/flys3@localhost"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/saar-mapfile.map	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,301 @@
+MAP
+    NAME "Saar"
+    STATUS ON
+    SIZE 600 400
+    MAXSIZE 4000
+    EXTENT 2539488.036000 5450928.892000 2575486.407000 5507352.839000
+    UNITS DD
+    FONTSET "fontset.txt"
+    SYMBOLSET './symbols/symbols.sym'
+    IMAGECOLOR 255 255 255
+    PROJECTION
+        "init=epsg:31466"
+    END
+
+
+    OUTPUTFORMAT
+        NAME agg
+        DRIVER AGG/PNG
+        IMAGEMODE RGB
+    END
+
+    CONFIG "MS_ERRORFILE" "logs/flys-saar-wms.log"
+    DEBUG 0
+
+    WEB
+      METADATA
+        "wms_title"             "FLYS-3.0 WMS (SAAR)"
+        "wms_onlineresource"    "http://czech-republic.atlas.intevation.de/cgi-bin/saar-wms"
+        "wms_accessconstraints" "none"
+        "wms_fees"              "none"
+        "wms_addresstype"       "postal"
+        "wms_address"           "Any Street"
+        "wms_city"              "Any City"
+        "wms_stateorprovince"   "Any state"
+        "wms_postcode"          "My Postalcode"
+        "wms_country"           "Any Country"
+        "wms_contactperson"     "Any Person"
+        "wms_contactorganization" "Any Orga"
+        "wms_contactelectronicmailaddress" "any-email@example.com"
+        "wms_contactvoicetelephone" "Any's telephone number"
+        "wms_srs" "EPSG:31466 EPSG:4326"
+        "wms_feature_info_mime_type" "text/html"
+        "ows_enable_request"   "*"
+      END
+    END
+
+    LAYER
+        NAME catchment 
+        EXTENT 2520667.897954 5376316.575645 2634771.191263 5508288.005707
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "catchment"
+        END
+
+        TYPE POLYGON
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM catchment USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "catchment"
+            STYLE
+                COLOR "#000080"
+                OUTLINECOLOR "#000000"
+            END
+        END
+    END
+    LAYER
+        NAME km 
+	GROUP km
+	EXTENT 2539489.068000 5450953.000500 2575482.527500 5507278.634500
+        DEBUG 0
+	DUMP TRUE
+  
+        METADATA
+            "wms_title" "km"
+        END
+
+        TYPE POINT 
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM river_axes_km USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "km"
+  	    STYLE
+             COLOR "#ff0000"
+  	     SYMBOL 'square'
+  	     SIZE 5
+  	   END
+        END
+    END
+
+    LAYER
+	NAME km_annotation 
+	GROUP km
+	EXTENT 2539489.068000 5450953.000500 2575482.527500 5507278.634500
+        DEBUG 5
+	DUMP TRUE
+  
+        METADATA
+            "wms_title" "km_annotation"
+        END
+  	
+	TYPE ANNOTATION 
+  	STATUS ON 
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM river_axes_km USING SRID 31466"
+        FILTER 'river_id=1'
+  	LABELITEM km
+
+        MAXSCALE 25000 
+
+  	CLASS 
+  	  LABEL 
+  	    ANGLE auto 
+  	    SIZE 10
+  	    COLOR "#000000" 
+  	    TYPE truetype 
+  	    FONT LiberationSans-Italic 
+	    POSITION ur
+	    OFFSET 2 2
+  	  END
+  	END 
+    END
+
+    LAYER
+        NAME buildings 
+        EXTENT 2540544.253718 5456266.217464 2567747.834199 5502557.982120
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "buildings (Bauwerke/Wehre)"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM buildings USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "buildings"
+            STYLE
+        	COLOR "#ff2222"
+            END
+        END
+    END
+    LAYER
+        NAME fixpoints 
+        EXTENT 2539388.036000 5450896.688000 2575586.296000 5507370.606000
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "fixpoints (Geodaesie/Festpunkte)"
+        END
+
+        TYPE POINT 
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM fixpoints USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "fixpoints"
+  	    STYLE
+             COLOR "#ffff00"
+  	     SYMBOL 'square'
+  	     SIZE 6
+  	   END
+        END
+    END
+    LAYER
+        NAME riveraxes
+        EXTENT 2539488.036000 5450928.892000 2575486.407000 5507352.839000
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "River Axes"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM river_axes USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "riveraxes"
+            STYLE
+       		 COLOR "#0000ff"
+            END
+        END
+    END
+    
+    LAYER
+        NAME qps
+        EXTENT 2539289.724000 5450852.896743 2576589.878311 5507289.656000
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "QPS (CrossSectionTracks)"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM cross_section_tracks USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+	MAXSCALEDENOM 100000
+
+        CLASS
+            NAME "qps"
+            STYLE
+        	COLOR "#0000ff"
+            END
+        END
+    END
+
+    LAYER
+        NAME hws 
+	EXTENT 2539778.101933 5456638.161347 2567463.841704 5500605.745332
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "HWS"
+        END
+
+        TYPE LINE
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM hws USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "hws"
+            STYLE
+        	COLOR "#ff2222" 
+            END
+        END
+    END
+    LAYER
+        NAME floodplain 
+        EXTENT 2539343.776823 5451397.340027 2576021.009478 5507230.640000
+        DEBUG 0
+  
+        METADATA
+            "wms_title" "floodplain (Hydr. Grenzen/Talaue)"
+        END
+
+        TYPE POLYGON
+        STATUS ON
+        INCLUDE "oracle_dbconnection.include"
+	DATA "GEOM FROM floodplain USING SRID 31466"
+        FILTER 'river_id=1'
+
+        PROJECTION
+            "init=epsg:31466"
+        END
+
+        CLASS
+            NAME "floodplain"
+            STYLE
+                COLOR "#800080"
+		OUTLINECOLOR "#000080"
+            END
+        END
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/mapserver/symbols/symbols.sym	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,22 @@
+SYMBOLSET
+SYMBOL
+  NAME 'point'
+  TYPE ELLIPSE
+  POINTS
+    1 1
+  END
+  FILLED TRUE
+END
+SYMBOL
+  NAME "square"
+  TYPE VECTOR
+  POINTS
+    0 0
+    0 1
+    1 1
+    1 0
+    0 0
+  END
+  FILLED TRUE
+END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/pom.xml	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,141 @@
+<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>1.1-beta-5</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>
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <version>1.3.158</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-dbcp</groupId>
+      <artifactId>commons-dbcp</artifactId>
+      <version>1.2.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt-shapefile</artifactId>
+      <version>2.7.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt-epsg-wkt</artifactId>
+      <version>2.7.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt-geojson</artifactId>
+      <version>2.7.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+      <version>1.7</version>
+    </dependency>
+  </dependencies>
+  <repositories>
+    <repository>
+        <id>jboss-repo2</id>
+        <name>JBoss repo2</name>
+        <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+    </repository>
+    <repository>
+      <id>gt2.repo</id>
+      <name>GeoTools2 Repository including JTS</name>
+      <url>http://download.osgeo.org/webdav/geotools</url>
+    </repository>
+  </repositories>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,273 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import net.sf.ehcache.Cache;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+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.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+import de.intevation.flys.artifacts.cache.CacheFactory;
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.model.AnnotationsFactory;
+
+import de.intevation.flys.model.Annotation;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+/**
+ * Artifact to access names of Points Of Interest along a segment of a river.
+ */
+public class AnnotationArtifact extends StaticFLYSArtifact {
+
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(AnnotationArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "annotation";
+
+    /* Name of cache. */
+    public static final String CACHE_NAME = "annotations";
+
+    @Override
+    protected void initialize(Artifact artifact, Object context,
+            CallMeta meta) {
+        logger.debug("AnnotationArtifact.initialize, id: "
+                + artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        addData("river", flys.getData("river"));
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        // TODO Add CallMeta (duplicate TODO in RiverAxisArtifact.java).
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+
+        if (!fs.isEmpty()) {
+            logger.debug("Facets to add in AnnotationsArtifact.initialize .");
+            facets.put(getCurrentStateId(), fs);
+        }
+        else {
+            logger.debug("No facets to add in AnnotationsArtifact.initialize .");
+        }
+    }
+
+
+    public double[] getDistance() {
+        /** TODO In initialize(), access maximal range of river (via
+         * AnnotationFactory) instead of overriding getDistance, 
+         * important for diagram generation. */
+        return new double[] {0f, 1000f};
+    }
+
+
+    /**
+     * Create the description of this AnnotationArtifact-instance.
+     *
+     * @param data Some data.
+     * @param context The CallContext.
+     *
+     * @return the description of this artifact.
+     */
+    @Override
+    public Document describe(Document data, CallContext context) {
+        logger.debug("Describe: the current state is: " + getCurrentStateId());
+
+        if (logger.isDebugEnabled()) {
+            dumpArtifact();
+        }
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine stateEngine = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        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);
+
+        Element name = ProtocolUtils.createArtNode(
+            creator, "name",
+            new String[] { "value" },
+            new String[] { getName() });
+
+        Element outs = ProtocolUtils.createArtNode(
+            creator, "outputmodes", null, null);
+        appendOutputModes(description, outs, context);
+
+        root.appendChild(name);
+        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;
+    }
+
+
+    /**
+     * Append outputmode elements to given document.
+     *
+     * @param doc Document to add outputmodes to.
+     * @param outs Element to add outputmode elements to.
+     * @param context The given CallContext (mostly for internationalization).
+     */
+    //@Override
+    protected void appendOutputModes(
+        Document    doc,
+        Element     outs,
+        CallContext context)
+    {
+        List<String> stateIds = getPreviousStateIds();
+
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        FLYSContext flysContext = FLYSUtils.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(doc, outs, generated);
+        }
+
+        try {
+            DefaultState cur = (DefaultState) getCurrentState(context);
+            if (cur.validate(this)) {
+                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(
+                                doc, outs, generated);
+                        }
+                        else{
+                            logger.debug("Cannot append output to generated document.");
+                        }
+                    }
+                    else {
+                        logger.debug("No facets found for the current state.");
+                    }
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // state is not valid, so we do not append its outputs.
+        }
+    } 
+
+
+    /**
+     * Get Annotations for Points (opposed to segments) in river in range.
+     *
+     * @return list of Annotations.
+     */
+    public List<Annotation> getAnnotations() {
+        String river = FLYSUtils.getRiver(this).getName();
+        logger.debug("Search annotations for river: " + river);
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+        String  key = river;
+        Object  old = null;
+
+        if (cache != null) {
+            logger.debug("We are using a cache for annotations.");
+
+            net.sf.ehcache.Element element = cache.get(key);
+            if (element != null) {
+                logger.info("Fetched annotations from cache.");
+                old = element.getValue();
+            }
+        }
+
+        if (old == null) {
+            old = getAnnotationsUncached(river);
+        }
+
+        if (cache != null && old != null) {
+            cache.put(new net.sf.ehcache.Element(key, old));
+        }
+
+        return old != null
+            ? (List<Annotation>) old
+            : new ArrayList<Annotation>();
+    }
+
+    /**
+     * Gets Annotations from Session/Database.
+     *
+     * @return List of Annotations fetched fresh from session/database.
+     * @see DistanceInfoService to access cached documents.
+     */
+    protected List<Annotation> getAnnotationsUncached(String river) {
+        logger.info("Fetch annotations from database.");
+
+        List<Annotation> annotations = new ArrayList<Annotation>();
+        annotations = AnnotationsFactory.getPointAnnotations(river);
+
+        return annotations;
+    }
+}
+// vim: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/CollectionMonitor.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,98 @@
+package de.intevation.flys.artifacts;
+
+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 de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.Hook;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.state.State;
+import de.intevation.artifactdatabase.state.Output;
+
+import de.intevation.flys.artifacts.datacage.Recommendations;
+
+
+public class CollectionMonitor implements Hook {
+
+    public static final String XPATH_RESULT = "/art:result";
+
+
+    private static final Logger logger =
+        Logger.getLogger(CollectionMonitor.class);
+
+
+    @Override
+    public void setup(Node cfg) {
+    }
+
+
+    @Override
+    public void execute(Artifact artifact, CallContext context, Document doc) {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        Element result = (Element) XMLUtils.xpath(
+            doc,
+            XPATH_RESULT,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        ElementCreator creator = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element recommended = creator.create("recommended-artifacts");
+        result.appendChild(recommended);
+
+        String[] outs              = extractOutputNames(flys, context);
+        Map<String, Object> params = getNoneUserSpecificParameters(flys, context);
+
+        Recommendations rec = Recommendations.getInstance();
+        rec.recommend(flys, null, outs, params, recommended);
+    }
+
+
+    public static String[] extractOutputNames(
+        FLYSArtifact flys,
+        CallContext  context)
+    {
+        State        state = flys.getCurrentState(context);
+        List<Output>  outs = state.getOutputs();
+
+        int num = outs == null ? 0 : outs.size();
+
+        String[] names = new String[num];
+
+        for (int i = 0; i < num; i++) {
+            names[i] = outs.get(i).getName();
+        }
+
+        return names;
+    }
+
+
+    protected Map<String, Object> getNoneUserSpecificParameters(
+        FLYSArtifact flys,
+        CallContext  context)
+    {
+        Map<String, Object> params = new HashMap<String, Object>(1);
+        params.put("recommended", "true");
+
+        return params;
+    }
+}
+// vim: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/FLYSArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,1063 @@
+package de.intevation.flys.artifacts;
+
+import de.intevation.artifactdatabase.ArtifactDatabaseImpl;
+import de.intevation.artifactdatabase.DefaultArtifact;
+
+import de.intevation.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+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.Artifact;
+import de.intevation.artifacts.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.xml.xpath.XPathConstants;
+
+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;
+
+/**
+ * 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";
+
+    public static final String XPATH_MODEL_ARTIFACT =
+        "/art:action/art:template/@uuid";
+
+    public static final String XPATH_FILTER =
+        "/art:action/art:filter/art:out";
+
+    /** 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 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;
+
+    /** Mapping of state names to created facets. */
+    protected Map<String, List<Facet>> facets;
+
+    /**
+     * Used to generates "view" on the facets (hides facets not matching the
+     * filter in output of collection);  out -&gt; facets.
+     */
+    protected Map<String, List<Facet>> filterFacets;
+
+
+    /**
+     * 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 String getName() {
+        return name;
+    }
+
+
+    /**
+     * 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,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("Setup this artifact with the uuid: " + identifier);
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+
+        List<State> states = getStates(context);
+
+        String name = getName();
+        logger.debug("Set initial state for artifact '" + name + "'");
+
+        setCurrentState(states.get(0));
+
+        String model = XMLUtils.xpathString(
+            data,
+            XPATH_MODEL_ARTIFACT,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (model != null && model.length() > 0) {
+            ArtifactDatabase db = (ArtifactDatabase) flysContext.get(
+                ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY);
+
+            try {
+                initialize(db.getRawArtifact(model), context, callMeta);
+            }
+            catch (ArtifactDatabaseException adbe) {
+                logger.error(adbe, adbe);
+            }
+        }
+
+        filterFacets = buildFilterFacets(data);
+    }
+
+
+    protected List<String> clonePreviousStateIds() {
+        return new ArrayList<String>(previousStateIds);
+    }
+
+
+    protected Map<String, StateData> cloneData() {
+        Map<String, StateData> copy = new TreeMap<String, StateData>();
+
+        for (Map.Entry<String, StateData> entry: data.entrySet()) {
+            copy.put(entry.getKey(), entry.getValue().deepCopy());
+        }
+
+        return copy;
+    }
+
+    /**
+     * Return a copy of the facet mapping.
+     * @return Mapping of state-ids to facets.
+     */
+    protected Map<String, List<Facet>> cloneFacets() {
+        Map copy = new HashMap<String, List<Facet>>();
+
+        for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
+            List<Facet> facets      = entry.getValue();
+            List<Facet> facetCopies = new ArrayList<Facet>(facets.size());
+            for (Facet facet: facets) {
+                facetCopies.add(facet.deepCopy());
+            }
+            copy.put(entry.getKey(), facetCopies);
+        }
+
+        return copy;
+    }
+
+
+    /**
+     * (called from setup).
+     * @param artifact master-artifact (if any, otherwise initialize is not called).
+     */
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        if (!(artifact instanceof FLYSArtifact)) {
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        currentStateId   = flys.currentStateId;
+        previousStateIds = flys.clonePreviousStateIds();
+        name             = flys.name;
+        data             = flys.cloneData();
+        facets           = flys.cloneFacets();
+        // Do not clone filter facets!
+    }
+
+
+    /**
+     * Builds filter facets from document.
+     * @see filterFacets
+     */
+    protected Map<String, List<Facet>> buildFilterFacets(Document document) {
+
+        NodeList nodes = (NodeList)XMLUtils.xpath(
+            document,
+            XPATH_FILTER,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (nodes == null || nodes.getLength() == 0) {
+            return null;
+        }
+
+        Map<String, List<Facet>> result = new HashMap<String, List<Facet>>();
+
+        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+            Element element = (Element)nodes.item(i);
+            String oName = element.getAttribute("name");
+            if (oName.length() == 0) {
+                continue;
+            }
+
+            List<Facet> facets = new ArrayList<Facet>();
+
+            NodeList facetNodes = element.getElementsByTagNameNS(
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                "facet");
+
+            for (int j = 0, M = facetNodes.getLength(); j < M; ++j) {
+                Element facetElement = (Element)facetNodes.item(j);
+
+                String fName = facetElement.getAttribute("name");
+
+                int index;
+                try {
+                    index = Integer.parseInt(facetElement.getAttribute("index"));
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn(nfe);
+                    index = 0;
+                }
+                facets.add(new DefaultFacet(index, fName, ""));
+            }
+
+            if (!facets.isEmpty()) {
+                result.put(oName, facets);
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * 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;
+
+            destroyStates(prevs, context);
+
+            for (int i = start; i >= targetIdx; i--) {
+                String prev = prevs.get(i);
+                logger.debug("Remove state id '" + prev + "'");
+
+                prevs.remove(prev);
+                facets.remove(prev);
+            }
+
+            destroyState(getCurrentStateId(), context);
+            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.
+     */
+    public State getCurrentState(Object context) {
+        return getState(context, getCurrentStateId());
+    }
+
+
+    /**
+     * Get list of existant states for this Artifact.
+     * @param context Contex to get StateEngine from.
+     * @return list of states.
+     */
+    protected List<State> getStates(Object context) {
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+        return engine.getStates(getName());
+    }
+
+
+    /**
+     * Get state with given ID.
+     * @param context Context to get StateEngine from.
+     * @param stateID ID of state to get.
+     * @return state with given ID.
+     */
+    protected State getState(Object context, String stateID) {
+        FLYSContext flysContext = FLYSUtils.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;
+    }
+
+
+    /**
+     * Get all previous and the current state id.
+     * @return #getPreviousStateIds() + #getCurrentStateId()
+     */
+    public List<String> getStateHistoryIds() {
+        List<String> allIds = getPreviousStateIds();
+        allIds.add(getCurrentStateId());
+        return allIds;
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    protected StateData removeData(String name) {
+        return this.data.remove(name);
+    }
+
+
+    /**
+     * 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 String getDataAsString(String name) {
+        StateData data = getData(name);
+        return data != null ? (String) data.getValue() : null;
+    }
+
+
+    /**
+     * Add StateData containing a given string.
+     * @param name Name of the data object.
+     * @param value String to store.
+     */
+    public void addStringData(String name, String value) {
+        addData(name, new DefaultStateData(name, null, null, value));
+    }
+
+
+    public Collection<StateData> getAllData() {
+        return data.values();
+    }
+
+
+    /**
+     * Get facet as stored internally, with equalling name and index than given
+     * facet.
+     * @param facet that defines index and name of facet searched.
+     * @return facet instance or null if not found.
+     */
+    public Facet getNativeFacet(Facet facet) {
+        String name  = facet.getName();
+        int    index = facet.getIndex();
+
+        for (Map.Entry<String, List<Facet>> facetList: facets.entrySet()) {
+            for (Facet f: facetList.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;
+
+        DefaultState current = (DefaultState) getCurrentState(context);
+
+        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, current.transform(this, context, name, value));
+            }
+            else if (name.length() > 0 && value.length() == 0) {
+                if (removeData(name) != null) {
+                    logger.debug("Removed data '" + name + "' successfully.");
+                }
+            }
+        }
+
+        current.validate(this);
+    }
+
+
+    /**
+     * 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 = FLYSUtils.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;
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    /**
+     * Return List of outputs, where combinations of outputname and filtername
+     * that match content in filterFacets is left out.
+     * @return filtered Outputlist.
+     */
+    protected List<Output> filterOutputs(List<Output> outs) {
+
+        if (filterFacets == null || filterFacets.isEmpty()) {
+            logger.debug("No filter for Outputs.");
+            return outs;
+        }
+
+        List<Output> filtered = new ArrayList<Output>();
+
+        for (Output out: outs) {
+
+            List<Facet> fFacets = filterFacets.get(out.getName());
+            if (fFacets != null) {
+
+                List<Facet> resultFacets = new ArrayList<Facet>();
+
+                for (Facet facet: out.getFacets()) {
+                    for (Facet fFacet: fFacets) {
+                        if (facet.getIndex() == fFacet.getIndex()
+                        &&  facet.getName().equals(fFacet.getName())) {
+                            resultFacets.add(facet);
+                            break;
+                        }
+                    }
+                }
+
+                if (!resultFacets.isEmpty()) {
+                    DefaultOutput nout = new DefaultOutput(
+                        out.getName(),
+                        out.getDescription(),
+                        out.getMimeType(),
+                        resultFacets);
+                    filtered.add(nout);
+                }
+            }
+        }
+
+        return filtered;
+    }
+
+
+    /**
+     * Get all outputs that the Artifact can do in this state (which includes
+     * all previous states).
+     *
+     * @return list of outputs
+     */
+    public List<Output> getOutputs(Object context) {
+        List<String> stateIds  = getPreviousStateIds();
+        List<Output> generated = new ArrayList<Output>();
+
+        for (String stateId: stateIds) {
+            DefaultState state = (DefaultState) getState(context, stateId);
+            generated.addAll(getOutputForState(state));
+        }
+
+        generated.addAll(getCurrentOutputs(context));
+
+        return filterOutputs(generated);
+    }
+
+
+    /**
+     * Get output(s) for current state.
+     * @return list of outputs for current state.
+     */
+    public List<Output> getCurrentOutputs(Object context) {
+        DefaultState cur = (DefaultState) getCurrentState(context);
+
+        try {
+            if (cur.validate(this)) {
+                return getOutputForState(cur);
+            }
+        }
+        catch (IllegalArgumentException iae) { }
+
+        return new ArrayList<Output>();
+    }
+
+
+    /**
+     * Get output(s) for a specific state.
+     * @param state State of interest
+     * @return list of output(s) for given state.
+     */
+    protected List<Output> getOutputForState(DefaultState state) {
+        List<Output> list = state.getOutputs();
+        if (list == null || list.size() == 0) {
+            logger.debug("-> No output modes for this state.");
+            return new ArrayList<Output>();
+        }
+
+        List<Facet> fs = facets.get(state.getID());
+
+        if (fs == null || fs.size() == 0) {
+            logger.debug("No facets found.");
+            return new ArrayList<Output>();
+        }
+
+        return generateOutputs(list, fs);
+    }
+
+
+    /**
+     * Generate a list of outputs with facets from fs if type is found in list
+     * of output.
+     *
+     * @param list List of outputs
+     * @param fs List of facets
+     */
+    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(),
+                out.getType());
+
+            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;
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * Like compute, but identify State by it id (string).
+     */
+    public Object compute(
+        CallContext context,
+        String      hash,
+        String      stateID,
+        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);
+    }
+
+
+    /**
+     * Let current state compute and register facets.
+     *
+     * @param key key of state
+     * @param state state
+     * @param type Type of compute
+     * @param generateFacets Whether new facets shall be generated.
+     */
+    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;
+                case INIT:
+                    res = state.computeInit(this, key, context, context.getMeta(), fs);
+                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 +++++++++++++++++");
+        }
+    }
+
+
+    protected void destroyState(String id, Object context) {
+        State s = getState(context, id);
+        s.endOfLife(this, context);
+    }
+
+
+    /**
+     * Calls endOfLife() for each state in the list <i>ids</i>.
+     *
+     * @param ids The State IDs that should be destroyed.
+     * @param context The FLYSContext.
+     */
+    protected void destroyStates(List<String> ids, Object context) {
+        for (int i = 0, num = ids.size(); i < num; i++) {
+            destroyState(ids.get(i), context);
+        }
+    }
+
+
+    /**
+     * Destroy the states.
+     */
+    @Override
+    public void endOfLife(Object context) {
+        logger.info("FLYSArtifact.endOfLife: " + identifier());
+
+        List<String> ids = getPreviousStateIds();
+        ids.add(getCurrentStateId());
+
+        destroyStates(ids, context);
+    }
+    
+    
+    /**
+     * Determines Facets initial disposition regarding activity (think of
+     * selection in Client ThemeList GUI). This will be checked one time
+     * when the facet enters a collections describe document.
+     *
+     * @param facetName  name of the facet.
+     * @param outputName name of the output.
+     * @param index      index of the facet.
+     *
+     * @return 1 if wished to be initally active, 0 if not. FLYSArtifact
+     *         defaults to "1".
+     */
+    public int getInitialFacetActivity(
+        String outputName,
+        String facetName,
+        int index)
+    {
+        return 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-artifacts/src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,315 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.MainValue;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.model.Calculation;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.MainValuesQFacet;
+import de.intevation.flys.artifacts.model.MainValuesWFacet;
+import de.intevation.flys.artifacts.model.NamedDouble;
+import de.intevation.flys.artifacts.model.WstValueTable;
+import de.intevation.flys.artifacts.model.WstValueTableFactory;
+
+import de.intevation.flys.artifacts.states.StaticState;
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+
+/**
+ * Artifact to access names of Points Of Interest along a segment of a river.
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class MainValuesArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(MainValuesArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "mainvalue";
+
+    /** The name of the static state for this artifact. */
+    public static final String STATIC_STATE_NAME = "state.mainvalue.static";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public MainValuesArtifact() {
+        logger.debug("MainValuesArtifact.MainValuesartifact()");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("MainValuesArtifact.setup");
+        state = new StaticState(STATIC_STATE_NAME);
+
+        Facet qfacet1 = new MainValuesQFacet(
+            COMPUTED_DISCHARGE_MAINVALUES_Q,
+            Resources.getMsg(
+                callMeta,
+                "facet.discharge_curves.mainvalues.q",
+                "facet.discharge_curves.mainvalues.q"));
+        Facet qfacet2 = new MainValuesQFacet(
+            MAINVALUES_Q,
+            Resources.getMsg(
+                callMeta,
+                "facet.discharge_curves.mainvalues.q",
+                "facet.discharge_curves.mainvalues.q"));
+        Facet wfacet1 = new MainValuesWFacet(
+            COMPUTED_DISCHARGE_MAINVALUES_W,
+            Resources.getMsg(
+                callMeta,
+                "facet.discharge_curves.mainvalues.w",
+                "facet.discharge_curves.mainvalues.w"));
+        Facet wfacet2 = new MainValuesWFacet(
+            MAINVALUES_W,
+            Resources.getMsg(
+                callMeta,
+                "facet.discharge_curves.mainvalues.w",
+                "facet.discharge_curves.mainvalues.w"));
+
+        List<Facet> fs = new ArrayList<Facet>();
+        fs.add(qfacet1);
+        fs.add(qfacet2);
+        fs.add(wfacet1);
+        fs.add(wfacet2);
+
+        facets.put(state.getID(), fs);
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Create "the" state.
+     */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        List<Facet> fs = (List<Facet>) facets.get(STATIC_STATE_NAME);
+
+        DefaultOutput mainValuesOutput2 = new DefaultOutput(
+            "computed_discharge_curve",
+            "output.computed_discharge_curve", "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(mainValuesOutput2);
+        return state;
+    }
+
+
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        logger.debug("MainValuesArtifact.initialize");
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        double location = FLYSUtils.getLocations(winfo)[0];
+        addData("location", new DefaultStateData("location", null, null,
+                    String.valueOf(location)));
+        addData("river", winfo.getData("river"));
+    }
+
+
+    /**
+     * Get a list containing the one and only State.
+     * @param  context ignored.
+     * @return list with one and only state.
+     */
+    @Override
+    protected List<State> getStates(Object context) {
+        ArrayList<State> states = new ArrayList<State>();
+        states.add(getState());
+        return states;
+    }
+
+
+    /**
+     * Get the "current" state.
+     * @param cc ignored.
+     * @return the "current" state.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        return getState();
+    }
+
+
+    /**
+     * Get the only possible state.
+     * @return the state.
+     */
+    protected State getState() {
+        return getState(null, null);
+    }
+
+
+    /**
+     * Get the state.
+     * @param context ignored.
+     * @param stateID ignored.
+     * @return the state.
+     */
+    @Override
+    protected State getState(Object context, String stateID) {
+        if (state != null)
+            return state;
+        else
+            return spawnState();
+    }
+
+
+    /**
+     * Access the Gauge that the mainvalues are taken from.
+     * @return Gauge that main values are taken from or null in case of
+     *         invalid parameterization.
+     */
+    protected Gauge getGauge() {
+        River river = FLYSUtils.getRiver(this);
+
+        if (river == null) {
+            return null;
+        }
+
+        double location = Double.parseDouble(
+                getDataAsString("location"));
+
+        return river.determineGaugeByPosition(location);
+    }
+
+
+    /**
+     * Get current location.
+     * @return the location.
+     */
+    public double getLocation() {
+        double location = Double.parseDouble(
+                (String)getData("location").getValue());
+        return location;
+    }
+
+
+    /**
+     * Get a list of "Q" main values.
+     * @return list of Q main values.
+     */
+    public List<NamedDouble> getMainValuesQ() {
+        List<NamedDouble> filteredList = new ArrayList<NamedDouble>();
+        Gauge gauge = getGauge();
+        WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this));
+        Calculation c = new Calculation();
+        double w_out[] = {0.0f};
+        double q_out[] = {0.0f};
+        double kms[] = {getLocation()};
+        double gaugeStation = gauge.getStation().doubleValue();
+        if (gauge != null) {
+            List<MainValue> orig = gauge.getMainValues();
+            for (MainValue mv : orig) {
+                if (mv.getMainValue().getType().getName().equals("Q")) {
+                    interpolator.interpolate(mv.getValue().doubleValue(),
+                            gaugeStation, kms, w_out, q_out, c);
+                    filteredList.add(new NamedDouble(
+                                mv.getMainValue().getName(),
+                                q_out[0]
+                                ));
+                }
+            }
+        }
+        return filteredList;
+    }
+
+
+    /**
+     * Get a list of "W" main values.
+     * @return list of W main values.
+     */
+    public List<NamedDouble> getMainValuesW() {
+        List<NamedDouble> filteredList = new ArrayList<NamedDouble>();
+        Gauge gauge = getGauge();
+        WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this));
+        Calculation c = new Calculation();
+        double gaugeStation = gauge.getStation().doubleValue();
+        double w_out[] = {0.0f};
+        double q_out[] = {0.0f};
+        double kms[] = {getLocation()};
+        if (gauge != null) {
+            List<MainValue> orig = gauge.getMainValues();
+            for (MainValue mv : orig) {
+                // We cannot interpolate the W values, so derive them
+                // from given Q values.
+                if (mv.getMainValue().getType().getName().equals("Q")) {
+                    interpolator.interpolate(mv.getValue().doubleValue(),
+                            gaugeStation, kms, w_out, q_out, c);
+                    filteredList.add(new NamedDouble(
+                                "W(" + mv.getMainValue().getName() +")",
+                                w_out[0]
+                                ));
+                }
+            }
+        }
+        return filteredList;
+    }
+
+
+    /**
+     * Determines Facets initial disposition regarding activity (think of
+     * selection in Client ThemeList GUI). This will be checked one time
+     * when the facet enters a collections describe document.
+     *
+     * @param facetName name of the facet.
+     * @param index     index of the facet.
+     * @return 0 if not active
+     */
+    @Override
+    public int getInitialFacetActivity(
+        String outputName,
+        String facetName,
+        int index)
+    {
+        logger.debug("MainValuesArtifact.active?: "
+           + outputName
+           + "/"
+           + facetName);
+
+        if (outputName.equals("computed_discharge_curve")) {
+            return 0;
+        }
+        else {
+            return 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-artifacts/src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,100 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.states.DefaultState;
+
+
+public class RiverAxisArtifact extends StaticFLYSArtifact {
+
+    public static final String NAME = "riveraxis";
+
+    public static final String XPATH_IDS = "/art:action/art:ids/@value";
+
+
+    private static final Logger logger =
+        Logger.getLogger(RiverAxisArtifact.class);
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("RiverAxisArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        String ids = XMLUtils.xpathString(
+            data, XPATH_IDS, ArtifactNamespaceContext.INSTANCE);
+
+        String[] splitted = ids != null ? ids.split(" ") : null;
+
+        if (splitted != null && splitted.length > 0) {
+            try {
+                int   river_id = Integer.parseInt(splitted[0]);
+                River river    = RiverFactory.getRiver(river_id);
+
+                if (river == null) {
+                    throw new IllegalArgumentException(
+                        "No river found for id: " + river_id);
+                }
+
+                String name = river.getName();
+
+                addData("river", new DefaultStateData("river",null,null,name));
+
+                List<Facet> fs = new ArrayList<Facet>();
+
+                DefaultState state = (DefaultState) getCurrentState(context);
+                state.computeInit(this, hash(), context, callMeta, fs);
+
+                if (!fs.isEmpty()) {
+                    facets.put(getCurrentStateId(), fs);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                logger.error("Could not create Artifact: " + nfe.getMessage());
+                throw new IllegalArgumentException("No river id given.");
+            }
+        }
+    }
+
+
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        // do nothing
+    }
+}
+// vim: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/StaticFLYSArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,79 @@
+package de.intevation.flys.artifacts;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.Output;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+public abstract class StaticFLYSArtifact extends FLYSArtifact {
+
+    private static final Logger logger =
+        Logger.getLogger(StaticFLYSArtifact.class);
+
+
+    @Override
+    public Document describe(Document data, CallContext cc) {
+        logger.debug("Describe artifact: " + identifier());
+
+        Document desc = XMLUtils.newDocument();
+
+        ElementCreator creator = new ElementCreator(
+            desc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ProtocolUtils.createRootNode(creator);
+        desc.appendChild(root);
+
+        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
+        root.appendChild(createOutputModes(cc, desc, creator));
+
+        return desc;
+    }
+
+
+    protected Element createOutputModes(
+        CallContext    cc,
+        Document       doc,
+        ElementCreator creator)
+    {
+        Element outs = ProtocolUtils.createArtNode(
+            creator, "outputmodes", null, null);
+
+        State state       = getCurrentState(cc);
+        List<Output> list = state.getOutputs();
+
+        if (list != null && list.size() > 0) {
+            List<Facet> fs = facets.get(state.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(
+                        doc, outs, generated);
+                }
+            }
+            else {
+                logger.debug("No facets found for the current state.");
+            }
+        }
+
+        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-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,237 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WKms;
+import de.intevation.flys.artifacts.model.WKmsFacet;
+import de.intevation.flys.artifacts.model.WKmsFactory;
+
+import de.intevation.flys.artifacts.states.StaticState;
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+/**
+ * Artifact to access additional "waterlevel"-type of data, like the height
+ * of protective measures (dikes).
+ *
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class StaticWKmsArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(StaticWKmsArtifact.class);
+
+    /** XPath to access initial parameter. */
+    public static final String XPATH_DATA =
+        "/art:action/art:ids/@value";
+
+    public static final String STATIC_STATE_NAME =
+        "state.additional_wkms.static";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public StaticWKmsArtifact() {
+        logger.debug("StaticWKmsArtifact.StaticWKmsArtifact");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("StaticWKmsArtifact.setup");
+
+        state = new StaticState(STATIC_STATE_NAME);
+
+        List<Facet> fs = new ArrayList<Facet>();
+        logger.debug(XMLUtils.toString(data));
+        String code = XMLUtils.xpathString(
+            data, XPATH_DATA, ArtifactNamespaceContext.INSTANCE);
+
+        // TODO Go for JSON, one day.
+        //ex.: flood_protection-wstv-114-12
+        if (code != null) {
+            String [] parts = code.split("-");
+
+            if (parts.length >= 4) {
+                int col = Integer.valueOf(parts[2]);
+                int wst = Integer.valueOf(parts[3]);
+
+                addStringData("col_pos", parts[2]);
+                addStringData("wst_id",  parts[3]);
+
+                String wkmsName = WKmsFactory.getWKmsName(col, wst);
+
+                String name;
+                if (parts[0].equals(HEIGHTMARKS_POINTS)) {
+                    name = HEIGHTMARKS_POINTS;
+                }
+                else {
+                    name = STATIC_WKMS;
+                }
+
+                Facet facet = new WKmsFacet(
+                    name,
+                    Resources.getMsg(
+                        callMeta,
+                        wkmsName,
+                        wkmsName));
+                fs.add(facet);
+                facets.put(state.getID(), fs);
+            }
+        }
+
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Initialize the static state with output.
+     * @return static state
+     */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        List<Facet> fs = facets.get(STATIC_STATE_NAME);
+        DefaultOutput output = new DefaultOutput(
+            "general",
+            "general", "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(output);
+        return state;
+    }
+
+
+    /**
+     * Called via setup.
+     *
+     * @param artifact The master-artifact.
+     */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        logger.debug("StaticWKmsArtifact.initialize");
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        // TODO: The river is of no interest, so far.
+        addData("river", winfo.getData("river"));
+    }
+
+
+    /**
+     * Get a list containing the one and only State.
+     * @param  context ignored.
+     * @return list with one and only state.
+     */
+    @Override
+    protected List<State> getStates(Object context) {
+        ArrayList<State> states = new ArrayList<State>();
+        states.add(getState());
+        return states;
+    }
+
+
+    /**
+     * Get the "current" state (there is but one).
+     * @param cc ignored.
+     * @return the "current" (only possible) state.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        return getState();
+    }
+
+
+    /**
+     * Get the only possible state.
+     * @return the state.
+     */
+    protected State getState() {
+        return getState(null, null);
+    }
+
+
+    /**
+     * Get the state.
+     * @param context ignored.
+     * @param stateID ignored.
+     * @return the state.
+     */
+    @Override
+    protected State getState(Object context, String stateID) {
+        return (state != null)
+            ? state
+            : spawnState();
+    }
+
+
+    /**
+     * Get WKms from factory.
+     * @param TODO idx param is not needed
+     * @return WKms according to parameterization (can be null);
+     */
+    public WKms getWKms(int idx) {
+        logger.debug("StaticWKmsArtifact.getWKms");
+
+        return WKmsFactory.getWKms(
+            Integer.valueOf(getDataAsString("col_pos")),
+            Integer.valueOf(getDataAsString("wst_id")));
+    }
+
+
+    /**
+     * Determines Facets initial disposition regarding activity (think of
+     * selection in Client ThemeList GUI). This will be checked one time
+     * when the facet enters a collections describe document.
+     *
+     * @param facetName name of the facet.
+     * @param index     index of the facet.
+     *
+     * @return Always 0. Static Data will enter plots inactive.
+     */
+    @Override
+    public int getInitialFacetActivity(
+        String outputName,
+        String facetName,
+        int index)
+    {
+        return 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/StaticWQKmsArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,247 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.WQKmsFacet;
+import de.intevation.flys.artifacts.model.WKmsFactory;
+import de.intevation.flys.artifacts.model.WQKmsFactory;
+
+import de.intevation.flys.artifacts.states.StaticState;
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+/**
+ * Artifact to access additional "waterlevel/discharge"-type of data, like
+ * fixation measurements.
+ *
+ * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
+ * with the StateEngine by overriding the getState*-methods.
+ */
+public class StaticWQKmsArtifact
+extends      StaticFLYSArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(StaticWQKmsArtifact.class);
+
+    /** XPath to access initial parameter. */
+    public static final String XPATH_DATA =
+        "/art:action/art:ids/@value";
+
+    public static final String STATIC_STATE_NAME =
+        "state.additional_wqkms.static";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public StaticWQKmsArtifact() {
+        logger.debug("StaticWQKmsArtifact.StaticWQKmsArtifact");
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("StaticWQKmsArtifact.setup");
+
+        state = new StaticState(STATIC_STATE_NAME);
+
+        List<Facet> fs = new ArrayList<Facet>();
+        logger.debug(XMLUtils.toString(data));
+        String code = XMLUtils.xpathString(
+            data, XPATH_DATA, ArtifactNamespaceContext.INSTANCE);
+
+        // TODO Go for JSON, one day.
+        //ex.: flood_protection-wstv-114-12
+        if (code != null) {
+            String [] parts = code.split("-");
+
+            if (parts.length >= 4) {
+                try {
+                    int col = Integer.valueOf(parts[2]);
+                    int wst = Integer.valueOf(parts[3]);
+
+                    addStringData("col_pos", parts[2]);
+                    addStringData("wst_id",  parts[3]);
+
+                    String wkmsName = WKmsFactory.getWKmsName(col, wst);
+
+                    String name;
+                    if (parts[0].equals(HEIGHTMARKS_POINTS)) {
+                        name = HEIGHTMARKS_POINTS;
+                    }
+                    else {
+                        name = STATIC_WQKMS;
+                    }
+
+                    Facet facet = new WQKmsFacet(
+                        name,
+                        Resources.getMsg(
+                            callMeta,
+                            wkmsName,
+                            wkmsName));
+                    fs.add(facet);
+                    facets.put(state.getID(), fs);
+                }
+                catch (Exception e) {}
+            }
+        }
+
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Initialize the static state with output.
+     * @return static state
+     */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME);
+        List<Facet> fs = facets.get(STATIC_STATE_NAME);
+        DefaultOutput output1 = new DefaultOutput(
+            "w_differences",
+            "w_differences.longitudinal_section", "image/png",
+            fs,
+            "chart");
+        DefaultOutput output2 = new DefaultOutput(
+            "longitudinal_section",
+            "longitudinal_section.longitudinal_section", "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(output1);
+        state.getOutputs().add(output2);
+        return state;
+    }
+
+
+    /**
+     * Called via setup.
+     *
+     * @param artifact The master-artifact.
+     */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        logger.debug("StaticWQKmsArtifact.initialize");
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        // TODO: The river is of no interest, so far.
+        addData("river", winfo.getData("river"));
+    }
+
+
+    /**
+     * Get a list containing the one and only State.
+     * @param  context ignored.
+     * @return list with one and only state.
+     */
+    @Override
+    protected List<State> getStates(Object context) {
+        ArrayList<State> states = new ArrayList<State>();
+        states.add(getState());
+        return states;
+    }
+
+
+    /**
+     * Get the "current" state (there is but one).
+     * @param cc ignored.
+     * @return the "current" (only possible) state.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        return getState();
+    }
+
+
+    /**
+     * Get the only possible state.
+     * @return the state.
+     */
+    protected State getState() {
+        return getState(null, null);
+    }
+
+
+    /**
+     * Get the state.
+     * @param context ignored.
+     * @param stateID ignored.
+     * @return the state.
+     */
+    @Override
+    protected State getState(Object context, String stateID) {
+        return (state != null)
+            ? state
+            : spawnState();
+    }
+
+
+    /**
+     * Get WQKms from factory.
+     * @param TODO idx param is not needed
+     * @return WQKms according to parameterization (can be null);
+     */
+    public WQKms getWQKms(int idx) {
+        logger.debug("StaticWQKmsArtifact.getWQKms");
+
+        return WQKmsFactory.getWQKms(
+            Integer.valueOf(getDataAsString("col_pos")),
+            Integer.valueOf(getDataAsString("wst_id")));
+    }
+
+
+    /**
+     * Determines Facets initial disposition regarding activity (think of
+     * selection in Client ThemeList GUI). This will be checked one time
+     * when the facet enters a collections describe document.
+     *
+     * @param facetName name of the facet.
+     * @param index     index of the facet.
+     *
+     * @return Always 0. Static Data will enter plots inactive.
+     */
+    @Override
+    public int getInitialFacetActivity(
+        String outputName,
+        String facetName,
+        int index)
+    {
+        return 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/WINFOArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,1333 @@
+package de.intevation.flys.artifacts;
+
+import java.awt.geom.Point2D;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+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.CallContext;
+import de.intevation.artifacts.Message;
+
+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.artifacts.context.FLYSContext;
+
+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.Calculation;
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.CrossSectionFactory;
+import de.intevation.flys.artifacts.model.DischargeTables;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.MainValuesFactory;
+import de.intevation.flys.artifacts.model.Segment;
+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.states.DefaultState;
+import de.intevation.flys.artifacts.states.LocationDistanceSelect;
+
+import de.intevation.flys.geom.Lines;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.CrossSection;
+import de.intevation.flys.model.CrossSectionLine;
+
+import de.intevation.flys.utils.DoubleUtil;
+import de.intevation.flys.utils.FLYSUtils;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+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.flys.artifacts.model.CalculationMessage;
+
+/**
+ * The default WINFO artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WINFOArtifact
+extends      FLYSArtifact
+implements   FacetTypes
+{
+
+    /** 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 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 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 = FLYSUtils.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);
+
+        appendBackgroundActivity(creator, root, context);
+
+        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 static void appendBackgroundActivity(
+        ElementCreator cr,
+        Element        root,
+        CallContext    context
+    ) {
+        Element inBackground = cr.create("background-processing");
+        root.appendChild(inBackground);
+
+        cr.addAttr(
+            inBackground,
+            "value",
+            String.valueOf(context.isInBackground()),
+            true);
+
+        LinkedList<Message> messages = context.getBackgroundMessages();
+
+        if (messages == null) {
+            return;
+        }
+
+        CalculationMessage  message  = (CalculationMessage) messages.getLast();
+        cr.addAttr(
+            inBackground,
+            "steps",
+            String.valueOf(message.getSteps()),
+            true);
+
+        cr.addAttr(
+            inBackground,
+            "currentStep",
+            String.valueOf(message.getCurrentStep()),
+            true);
+
+        inBackground.setTextContent(message.getMessage());
+    }
+
+
+    /**
+     * Append output mode nodes to a document.
+     */
+    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 = FLYSUtils.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 for previous state found.");
+                continue;
+            }
+
+            logger.debug("Found " + fs.size() + " facets in previous states.");
+
+            List<Output> generated = generateOutputs(list, fs);
+
+            ProtocolUtils.appendOutputModes(doc, outs, generated);
+        }
+
+        try {
+            DefaultState cur = (DefaultState) getCurrentState(context);
+            if (cur.validate(this)) {
+                List<Output> list = cur.getOutputs();
+                if (list != null && list.size() > 0) {
+                    logger.debug(
+                        "Append output modes for current 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(
+                                doc, 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.
+        }
+    }
+
+
+    /**
+     * 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 = FLYSUtils.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 = FLYSUtils.getRiver(this);
+        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 = FLYSUtils.getKmRange(this);
+        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 = FLYSUtils.getRiver(this);
+
+        if (r == null) {
+            return error(null, "Cannot determine river.");
+        }
+
+        Gauge g = getGauge();
+
+        if (g == null) {
+           return error(null, "Cannot determine gauge.");
+        }
+
+        double[] locations = FLYSUtils.getLocations(this);
+
+        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 = FLYSUtils.getRiver(this);
+        if (river == null) {
+            return error(new WQKms[0], "no river found");
+        }
+
+        double [] distance = FLYSUtils.getKmRange(this);
+
+        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 = FLYSUtils.getRiver(this);
+
+        if (r == null) {
+            return error(new WQKms[0], "Cannot determine river.");
+        }
+
+        double[] locations = FLYSUtils.getLocations(this);
+
+        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 = FLYSUtils.getRiver(this);
+        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]);
+    }
+
+
+    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);
+    }
+
+
+    /**
+     * Get List of all cross-sections for current river.
+     *
+     * @return List of CrossSections for current river, null in case of error.
+     */
+    protected List<CrossSection> getCrossSections() {
+        River river = FLYSUtils.getRiver(this);
+        if (river == null) {
+            logger.warn("No river in WINFO found");
+            return null;
+        }
+        return CrossSectionFactory.getCrossSections(river);
+    }
+
+
+    /**
+     * Get CrossSectionLine spatially closest to what is specified in the data
+     * "cross_section.km" (and from "cross_section.index").
+     *
+     * @return CrossSectionLine closest to "cross_section.km".
+     */
+    protected CrossSectionLine searchCrossSectionKmLine() {
+        double wishKM = 0.0f;
+        try {
+            wishKM = Double.parseDouble(getDataAsString("cross_section.km"));
+        }
+        catch (Exception e) {
+            logger.warn("Exception when parsing cross_section.km", e);
+        }
+
+        int crossSectionIdx = 0;
+
+        List<CrossSection> sections = getCrossSections();
+        if (sections.size() < crossSectionIdx) {
+            logger.error("Cross-section not found: " + crossSectionIdx);
+            return null;
+        }
+        if (sections.size() == 0
+            || sections.get(crossSectionIdx).getLines().size() == 0)
+        {
+            return null;
+        }
+
+        // Get the cross section closest to requested km.
+        // Naive, linear approach.
+        List<CrossSectionLine> crossSectionLines =
+            sections.get(crossSectionIdx).getLines();
+        CrossSectionLine oldLine = crossSectionLines.get(0);
+        double oldDiff = Math.abs(wishKM - oldLine.getKm().doubleValue());
+        for (CrossSectionLine line: crossSectionLines) {
+            double diff = Math.abs(wishKM - line.getKm().doubleValue());
+            if (diff > oldDiff) {
+                break;
+            }
+            oldDiff = diff;
+            oldLine = line;
+        }
+        return oldLine;
+    }
+
+
+    /**
+     * Get km for which a CrossSection is actually available (this may vary
+     * from the user picked "cross_section.km" data).
+     *
+     * @return km for which cross section is calculated.
+     */
+    public double getCrossSectionSnapKm() {
+        // Note that this is this triggers a linear search.
+        CrossSectionLine line = searchCrossSectionKmLine();
+        if (line == null) {
+            logger.warn("No Cross Section for given km found.");
+            return 0.0f;
+        }
+        else {
+            return line.getKm().doubleValue();
+        }
+    }
+
+
+    /**
+     * Get points of Profile of cross section.
+     *
+     * @return an array holding coordinates of points of profile (
+     *         in the form {{x1, x2} {y1, y2}} ).
+     */
+    public double [][] getCrossSectionData() {
+        logger.info("getCrossSectionData() for cross_section.km "
+            + getDataAsString("cross_section.km"));
+        CrossSectionLine line = searchCrossSectionKmLine();
+
+        return line != null
+               ? line.fetchCrossSectionProfile()
+               : null;
+    }
+
+
+    /**
+     * Get points of line describing the surface of water at cross section.
+     *
+     * @return an array holding coordinates of points of surface of water (
+     *         in the form {{x1, x2} {y1, y2}} ).
+     */
+    public double [][] getWaterLines(int idx) {
+        logger.debug("getWaterLines()");
+        CrossSectionLine csl = searchCrossSectionKmLine();
+        List<Point2D> points = csl.fetchCrossSectionLinesPoints();
+
+        // Need W at km
+        WQKms [] wqkms = (WQKms[]) getWaterlevelData().getData();
+        if (wqkms.length == 0) {
+            logger.error("No WQKms found.");
+            return Lines.createWaterLines(points, 0.0f);
+        }
+
+        if (wqkms.length < idx) {
+            logger.error("getWaterLines() requested index ("
+                         + idx + " not found.");
+        }
+
+        // Find W at km, linear naive approach.
+        WQKms triple = wqkms[idx];
+
+        // Find index of km.
+        double wishKM = 0.0f;
+        int old_idx = 0;
+        try {
+            wishKM = Double.parseDouble(getDataAsString("cross_section.km"));
+        }
+        catch (Exception e) {
+            logger.warn("Exception when trying to get cross_section.km", e);
+        }
+
+        if (triple.size() == 0) {
+            logger.warn("Calculation of waterline is empty.");
+            return Lines.createWaterLines(points, 0.0f);
+        }
+
+        // Linear seach in WQKms for closest km.
+        double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
+        double last_w = triple.getW(0);
+
+        for (int i = 0, T = triple.size(); i < T; i++) {
+            double diff = Math.abs(wishKM - triple.getKm(i));
+            if (diff > old_dist_wish) {
+                break;
+            }
+            last_w = triple.getW(i);
+            old_dist_wish = diff;
+        }
+        return Lines.createWaterLines(points, last_w);
+    }
+
+
+    /**
+     * Get name of cross sections.
+     * @return list of names of cross-sections.
+     */
+    public List<String> getCrossSectionNames() {
+        List<String> names = new ArrayList<String>();
+
+        for (CrossSection section: getCrossSections()) {
+            names.add(section.getDescription());
+        }
+
+        return names;
+    }
+
+
+    /**
+     * 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 = FLYSUtils.getRiver(this);
+        if (r == null) {
+            logger.warn("no river found");
+            return null;
+        }
+
+        double [] range = FLYSUtils.getKmRange(this);
+        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;
+    }
+
+
+    /**
+     * 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 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 = FLYSUtils.getKmRange(this);
+            return getKms(distance);
+
+        }
+        else {
+            return LocationDistanceSelect.getLocations(this);
+        }
+    }
+
+
+    public double [] getFromToStep() {
+        if (!isRange()) {
+            return null;
+        }
+        double [] fromTo = FLYSUtils.getKmRange(this);
+
+        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 = FLYSUtils.getRiver(this);
+
+        if (river == null) {
+            logger.debug("no river found");
+            return null;
+        }
+
+        double[] dist  = FLYSUtils.getKmRange(this);
+
+        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 = FLYSUtils.getRiver(this);
+        if (river == null) {
+            return null;
+        }
+
+        double [] dist = FLYSUtils.getKmRange(this);
+        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;
+        }
+    }
+
+    /**
+     * 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();
+    }
+
+
+    /**
+     * Determines Facets initial disposition regarding activity (think of
+     * selection in Client ThemeList GUI). This will be checked one time
+     * when the facet enters a collections describe document.
+     *
+     * @param facetName name of the facet.
+     * @param index     index of the facet.
+     * @return 0 if not active
+     */
+    @Override
+    public int getInitialFacetActivity(String outputName, String facetName, int index) {
+        String [] inactives = new String[] {
+                                            LONGITUDINAL_Q
+                                            };
+
+        logger.debug("WINFOArtifact.active?: "
+            + outputName
+            + "/"
+            + facetName);
+
+        if ((facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_Q) ||
+             facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+             && outputName.equals("computed_discharge_curve")))
+            {
+                return 0;
+            }
+        return Arrays.asList(inactives).contains(facetName)
+               ? 0
+               : 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-artifacts/src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,48 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+
+
+public class WMSBackgroundArtifact extends StaticFLYSArtifact {
+
+    public static final String NAME = "wmsbackground";
+
+    private static final Logger logger =
+        Logger.getLogger(WMSBackgroundArtifact.class);
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    protected void initialize(Artifact artifact, Object context, CallMeta meta) {
+        logger.debug("Initialize internal state with: "+ artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        addData("river", flys.getData("river"));
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        // TODO Add CallMeta
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+
+        if (!fs.isEmpty()) {
+            facets.put(getCurrentStateId(), fs);
+        }
+    }
+}
+// vim: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/WMSDBArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,152 @@
+package de.intevation.flys.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+import de.intevation.flys.artifacts.model.WMSDBLayerFacet;
+
+
+public abstract class WMSDBArtifact extends StaticFLYSArtifact {
+
+    private static final Logger logger = Logger.getLogger(WMSDBArtifact.class);
+
+    public static final String XPATH_IDS = "/art:action/art:ids/@value";
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSDBArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+
+        String ids = XMLUtils.xpathString(
+            data, XPATH_IDS, ArtifactNamespaceContext.INSTANCE);
+
+        if (ids != null && ids.length() > 0) {
+            addData("ids", new DefaultStateData("ids", null, null, ids));
+        }
+        else {
+            throw new IllegalArgumentException("No attribute 'ids' found!");
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+
+        WMSDBState state = (WMSDBState) getCurrentState(context);
+        state.computeInit(this, hash(), context, callMeta, fs);
+
+        if (!fs.isEmpty()) {
+            facets.put(getCurrentStateId(), fs);
+        }
+    }
+
+
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object   context,
+        CallMeta callMeta)
+    {
+        // do nothing
+    }
+
+
+    /**
+     * Get a list containing the one and only State.
+     * @param  context ignored.
+     * @return list with one and only state.
+     */
+    @Override
+    protected List<State> getStates(Object context) {
+        ArrayList<State> states = new ArrayList<State>();
+        states.add(getCurrentState(context));
+
+        return states;
+    }
+
+
+
+    public static abstract class WMSDBState extends DefaultState {
+        private static final Logger logger = Logger.getLogger(WMSDBState.class);
+
+        protected WMSDBArtifact artifact;
+
+        public WMSDBState(WMSDBArtifact artifact) {
+            this.artifact = artifact;
+        }
+
+        @Override
+        public Object computeInit(
+            FLYSArtifact artifact,
+            String       hash,
+            Object       context,
+            CallMeta     meta,
+            List<Facet>  facets
+        ) {
+            logger.debug("WMSDBState.computeInit");
+
+            String type = getFacetType();
+
+            WMSDBLayerFacet facet = new WMSDBLayerFacet(
+                0,
+                type,
+                getTitle(meta),
+                ComputeType.INIT,
+                getID(), hash,
+                getUrl());
+
+            String name = artifact.identifier() + "-" + type;
+
+            facet.addLayer(name);
+            facet.setExtent(getExtent());
+            facet.setSrid(getSrid());
+            facet.setData(getDataString());
+            facet.setFilter(getFilter());
+            facet.setGeometryType(getGeometryType());
+
+            facets.add(facet);
+
+            return null;
+        }
+
+        protected abstract String getFacetType();
+
+        protected abstract String getTitle(CallMeta meta);
+
+        protected abstract String getUrl();
+
+        protected abstract String getSrid();
+
+        protected abstract Envelope getExtent();
+
+        protected abstract String getFilter();
+
+        protected abstract String getDataString();
+
+        protected abstract String getGeometryType();
+    } // end of WMSDBState
+}
+// vim: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/WMSKmArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,161 @@
+package de.intevation.flys.artifacts;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.RiverAxisKm;
+
+import de.intevation.flys.artifacts.WMSDBArtifact.WMSDBState;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.utils.FLYSUtils;
+
+
+public class WMSKmArtifact extends WMSDBArtifact {
+
+    public static final String NAME = "wmskm";
+
+
+    private static final Logger logger = Logger.getLogger(WMSKmArtifact.class);
+
+
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("WMSKmArtifact.setup");
+
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    @Override
+    public State getCurrentState(Object cc) {
+        State s = new WMSKmState(this);
+
+        List<Facet> fs = facets.get(getCurrentStateId());
+
+        DefaultOutput o = new DefaultOutput(
+            "floodmap",
+            "floodmap",
+            "image/png",
+            fs,
+            "map");
+
+        s.getOutputs().add(o);
+
+        return s;
+    }
+
+
+
+    public static class WMSKmState extends WMSDBState implements FacetTypes {
+
+        private static final Logger logger = Logger.getLogger(WMSKmState.class);
+
+        protected Geometry geom;
+        protected int      riverId;
+
+        public WMSKmState(WMSDBArtifact artifact) {
+            super(artifact);
+            riverId = 0;
+        }
+
+        public int getRiverId() {
+            if (riverId == 0) {
+                String ids = artifact.getDataAsString("ids");
+
+                try {
+                    riverId = Integer.valueOf(ids);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.error("Cannot parse river id from '" + ids + "'");
+                }
+            }
+
+            return riverId;
+        }
+
+        @Override
+        protected String getFacetType() {
+            return FLOODMAP_KMS;
+        }
+
+        @Override
+        protected String getTitle(CallMeta meta) {
+            return Resources.getMsg(meta, FLOODMAP_KMS, FLOODMAP_KMS);
+        }
+
+        @Override
+        protected String getUrl() {
+            return FLYSUtils.getUserWMSUrl(artifact.identifier());
+        }
+
+        @Override
+        protected String getSrid() {
+            River river = RiverFactory.getRiver(getRiverId());
+            return FLYSUtils.getRiverSrid(river.getName());
+        }
+
+        @Override
+        protected Envelope getExtent() {
+            List<RiverAxisKm> kms = RiverAxisKm.getRiverAxisKms(getRiverId());
+
+            Envelope max = null;
+
+            for (RiverAxisKm km: kms) {
+                Envelope env = km.getGeom().getEnvelopeInternal();
+
+                if (max == null) {
+                    max = env;
+                    continue;
+                }
+
+                max.expandToInclude(env);
+            }
+
+            return max;
+        }
+
+        @Override
+        protected String getFilter() {
+            return "river_id=" + String.valueOf(getRiverId());
+        }
+
+        @Override
+        protected String getDataString() {
+            return "geom FROM river_axes_km USING UNIQUE id USING SRID 31466";
+        }
+
+        @Override
+        protected String getGeometryType() {
+            return "POINT";
+        }
+    } // end of WMSKmState
+}
+// vim: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/WaterlevelArtifact.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,117 @@
+package de.intevation.flys.artifacts;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+
+/**
+ * Clone of an WINFOArtifact to expose exactly one waterlevel only.
+ * All Facets of the "longitudinal_section" output will be added to the
+ * "w_differences" output and filterFacets adjusted accordingly.
+ *
+ * @TODO Straighten inheritance-line (waterlevel-WINFO or vice versa).
+ */
+public class WaterlevelArtifact extends WINFOArtifact {
+
+    /** The logger for this class. */
+    private static Logger logger = Logger.getLogger(WaterlevelArtifact.class);
+
+    /** The name of the artifact. */
+    public static final String ARTIFACT_NAME = "waterlevel";
+
+
+    /**
+     * The default constructor.
+     */
+    public WaterlevelArtifact() {
+    }
+
+
+    /**
+     * Setup and restate longitudinal_section filterfacets to apply to the
+     * w_differences output, too.
+     */
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        super.setup(identifier, factory, context, callMeta, data);
+        if(filterFacets != null) {
+            filterFacets.put(
+                "w_differences",
+                filterFacets.get("longitudinal_section"));
+        }
+    }
+
+    /**
+     * Clone important stuff of an WINFOArtifact.
+     * @param artifact the WINFOArtifact to clone stuff from.
+     */
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        this.data = winfo.cloneData();
+	logger.debug("Cloned data of winfo artifact.");
+        // Statically add Facets.
+        List<Facet> fs = new ArrayList<Facet>();
+        DefaultState state = (DefaultState) getCurrentState(context);
+        state.computeInit(this, hash(), context, meta, fs);
+        if (!fs.isEmpty()) { 
+            logger.debug("Facets to add in WaterlevelArtifact.initialize ."); 
+            facets.put(getCurrentStateId(), fs); 
+        } 
+        else { 
+            logger.debug("No facets to add in WaterlevelArtifact.initialize ("
+                + state.getID() + ").");
+        }
+    }
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+    /**
+     * Determines Facets initial disposition regarding activity (think of
+     * selection in Client ThemeList GUI).
+     * WaterlevelArtifact Facets should come to live "inactive" (always
+     * return 0).
+     *
+     * @param facetName name of the facet.
+     * @param index     index of the facet.
+     *
+     * @return Always 0 to have Facets initial predisposition to "inactive".
+     */
+    @Override
+    public int getInitialFacetActivity(
+        String outputName,
+        String facetName,
+        int index)
+    {
+        return 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-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,79 @@
+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 CACHE_CONFIG_FILE_PROPERTY =
+        "flys.artifacts.cache.config.file";
+
+    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 String getConfigFile() {
+        String configFile = System.getProperty(CACHE_CONFIG_FILE_PROPERTY);
+
+        if (configFile != null) {
+            return configFile;
+        }
+
+        configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE);
+
+        if (configFile != null) {
+            configFile = Config.replaceConfigDir(configFile);
+        }
+
+        return configFile;
+    }
+
+    public static final synchronized Cache getCache(String cacheName) {
+        if (!initialized) {
+            initialized = true; // try only once
+            String configFile = getConfigFile();
+            if (configFile != null) {
+                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/charts/CrossSectionApp.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,536 @@
+package de.intevation.flys.artifacts.charts;
+
+import de.intevation.flys.backend.SessionFactoryProvider;
+
+import de.intevation.flys.geom.Lines;
+
+import de.intevation.flys.model.CrossSection;
+import de.intevation.flys.model.CrossSectionLine;
+import de.intevation.flys.model.CrossSectionPoint;
+
+import de.intevation.flys.utils.Pair;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import java.awt.geom.Point2D;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import java.math.MathContext;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+import javax.swing.table.AbstractTableModel;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+
+import org.jfree.chart.axis.NumberAxis;
+
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.jfree.ui.ApplicationFrame;
+import org.jfree.ui.RefineryUtilities;
+
+public class CrossSectionApp
+extends      ApplicationFrame
+{
+    public static final String RIVER = System.getProperty("river", "Saar");
+
+    public static final double EPSILON   = 1e-4;
+
+    protected Session session;
+
+    protected JComboBox crossSectionLinesCB;
+    protected JTextField waterlevelTF;
+
+    protected ChartPanel chartPanel;
+
+    protected Double lastWaterLevel;
+
+    protected List<CrossSection> crossSections;
+    protected boolean [] drawCrossSection;
+    protected boolean [] drawWaterLevel;
+    protected boolean [] drawGround;
+
+    protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines;
+
+    public class CrossSectionTableModel extends AbstractTableModel {
+
+        @Override
+        public String getColumnName(int col) {
+            switch (col) {
+                case 0: return "Peilungsname";
+                case 1: return "Peilung";
+                case 2: return "Wasserstand";
+                case 3: return "Boden";
+            }
+            return "";
+        }
+
+        @Override
+        public int getColumnCount() {
+            return 4; 
+        }
+
+        @Override
+        public int getRowCount() {
+            return crossSections != null ? crossSections.size() : 0;
+        }
+
+        @Override
+        public Object getValueAt(int row, int col) {
+            if (crossSections == null) return null;
+            switch (col) {
+                case 0: return crossSections.get(row).getDescription();
+                case 1: return drawCrossSection[row];
+                case 2: return drawWaterLevel[row];
+                case 3: return drawGround[row];
+            }
+            return null;
+        }
+
+        @Override
+        public void setValueAt(Object value, int row, int col) {
+            switch (col) {
+                case 1: 
+                    if (change(drawCrossSection, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+                case 2:
+                    if (change(drawWaterLevel, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+                case 3:
+                    if (change(drawGround, row, (Boolean)value)) {
+                        fireTableCellUpdated(row, col);
+                    }
+                    break;
+            }
+        }
+
+        @Override
+        public Class<?> getColumnClass(int columnIndex) {
+            switch (columnIndex) {
+                case 0: return String.class;
+                case 1: return Boolean.class;
+                case 2: return Boolean.class;
+                case 3: return Boolean.class;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isCellEditable(
+            int rowIndex,
+            int columnIndex
+        ) {
+            return columnIndex >= 1 && columnIndex <= 3;
+        }
+    } // class CrossSectionTableModel
+
+    private static boolean change(
+        boolean [] values,
+        int        index, 
+        boolean    value
+    ) {
+        if (values[index] != value) {
+            values[index] = value;
+            return true;
+        }
+        return false;
+    }
+
+    public static class CrossSectionLineItem {
+
+        Double km;
+        List<Pair<CrossSection, CrossSectionLine>> lines;
+
+        public CrossSectionLineItem(
+            Double km, 
+            List<Pair<CrossSection, CrossSectionLine>> lines
+        ) {
+            this.km    = km;
+            this.lines = lines;
+        }
+
+        public String toString() {
+            return String.valueOf(km);
+        }
+    } // CrossSectionLineItem
+
+    public CrossSectionApp(String title) {
+        super(title);
+
+        session = SessionFactoryProvider
+            .createSessionFactory()
+            .openSession();
+
+        JPanel content = createContent();
+        content.setPreferredSize(new Dimension(800, 480));
+        setContentPane(content);
+
+    }
+
+    public List<CrossSection> crossSections(String river) {
+        Query query = session.createQuery(
+            "from CrossSection where river.name = :river");
+        query.setParameter("river", river);
+        return query.list();
+    }
+
+    protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> loadAllLines(
+        List<CrossSection> crossSections
+    ) {
+        Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines =
+            new TreeMap<Double, List<Pair<CrossSection, CrossSectionLine>>>();
+        for (CrossSection cs: crossSections) {
+            List<CrossSectionLine> lines = cs.getLines();
+            for (CrossSectionLine csl: lines) {
+                Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d;
+                List<Pair<CrossSection, CrossSectionLine>> ls = km2lines.get(km);
+                if (ls == null) {
+                    ls = new ArrayList<Pair<CrossSection, CrossSectionLine>>(2);
+                    km2lines.put(km, ls);
+                }
+                ls.add(new Pair<CrossSection, CrossSectionLine>(cs, csl));
+            }
+        }
+        return km2lines;
+    }
+
+    public JPanel createContent() {
+        JPanel panel = new JPanel(new BorderLayout());
+
+
+        JPanel nav = new JPanel(new FlowLayout());
+
+        crossSections = crossSections(RIVER);
+        km2lines = loadAllLines(crossSections);
+        drawCrossSection = new boolean[crossSections.size()];
+        Arrays.fill(drawCrossSection, true);
+
+        drawWaterLevel = new boolean[crossSections.size()];
+        drawGround     = new boolean[crossSections.size()];
+
+        Object [] clis = createCrossSectionLineItems(km2lines);
+
+        DefaultComboBoxModel dcbm = new DefaultComboBoxModel(clis);
+
+        crossSectionLinesCB = new JComboBox(dcbm);
+
+        nav.add(crossSectionLinesCB);
+
+        crossSectionLinesCB.addItemListener(new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent ie) {
+                if (ie.getStateChange() == ItemEvent.SELECTED) {
+                    updateChart();
+                }
+            }
+        });
+
+        waterlevelTF = new JTextField(5);
+
+        waterlevelTF.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                waterLevelChanged();
+            }
+        });
+
+        nav.add(waterlevelTF);
+
+        JButton dump = new JButton("dump");
+
+        dump.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                dumpData();
+            }
+        });
+
+        nav.add(dump);
+
+
+        chartPanel = createChartPanel();
+
+        panel.add(chartPanel, BorderLayout.CENTER);
+
+
+        CrossSectionTableModel cstm = new CrossSectionTableModel();
+
+        cstm.addTableModelListener(new TableModelListener() {
+            @Override
+            public void tableChanged(TableModelEvent e) {
+                updateChart();
+            }
+        });
+
+        JTable crossTable = new JTable(cstm);
+
+        JPanel west = new JPanel(new BorderLayout());
+        JScrollPane scrollPane = new JScrollPane(crossTable);
+        west.add(scrollPane);
+
+        west.add(nav, BorderLayout.SOUTH);
+
+        panel.add(west, BorderLayout.WEST);
+
+        return panel;
+    }
+
+    protected void waterLevelChanged() {
+        String value = waterlevelTF.getText();
+        try {
+            lastWaterLevel = Double.parseDouble(value);
+        }
+        catch (NumberFormatException nfe) {
+            waterlevelTF.setText(
+                lastWaterLevel != null ? lastWaterLevel.toString() : "");
+            return;
+        }
+        updateChart();
+    }
+
+    protected void updateChart() {
+
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        JFreeChart chart = createChart(csli == null
+            ? new XYSeriesCollection()
+            : generateDataset());
+
+        chartPanel.setChart(chart);
+    }
+
+    protected ChartPanel createChartPanel() {
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        JFreeChart chart = createChart(csli == null
+            ? new XYSeriesCollection()
+            : generateDataset());
+
+        return new ChartPanel(chart);
+    }
+
+    protected void dumpData() {
+
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        if (csli == null) {
+            return;
+        }
+
+
+        double km = Math.round(csli.km.doubleValue() * 1000d)/1000d;
+
+        String kmS = String.valueOf(km).replace(".", "-");
+
+        int i = 1;
+        File file = new File("cross-section-" + kmS + ".txt");
+        while (file.exists()) {
+            file = new File("cross-section-" + kmS + "[" + (i++) + "].txt");
+        }
+
+        System.err.println("dump points to file '" + file + "'");
+
+        PrintWriter out = null;
+
+        MathContext mc = new MathContext(3);
+
+        try {
+            out =
+                new PrintWriter(
+                new FileWriter(file));
+
+            for (Pair<CrossSection, CrossSectionLine> pair: csli.lines) {
+                out.println("# " + pair.getA().getDescription());
+                for (CrossSectionPoint point: pair.getB().getPoints()) {
+                    out.println(
+                        point.getX().round(mc) + " " +
+                        point.getY().round(mc));
+                }
+            }
+
+            out.flush();
+        }
+        catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+        finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    public void generateWaterLevels(
+        List<Point2D>      points,
+        XYSeriesCollection collection
+    ) {
+        if (points == null || points.isEmpty()) {
+            return;
+        }
+
+        if (lastWaterLevel != null) {
+            double [][] data = Lines.createWaterLines(points, lastWaterLevel);
+            XYSeries series =
+                new XYSeries(String.valueOf(lastWaterLevel), false);
+
+            double [] x = data[0];
+            double [] y = data[1];
+            for (int i = 0; i < x.length; ++i) {
+                series.add(x[i], y[i], false);
+            }
+
+            collection.addSeries(series);
+        }
+    }
+
+    public void generateProfile(
+        List<Point2D>      points,
+        String             legend,
+        XYSeriesCollection collection
+    ) {
+        if (points == null || points.isEmpty()) {
+            return;
+        }
+
+        double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
+
+        XYSeries series = new XYSeries(legend, false);
+
+        double [] x = values[0];
+        double [] y = values[1];
+        for (int i = 0; i < x.length; ++i) {
+            series.add(x[i], y[i], false);
+        }
+
+        collection.addSeries(series);
+    }
+
+    public XYSeriesCollection generateDataset() {
+        XYSeriesCollection collection = new XYSeriesCollection();
+
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        for (int i = 0; i < drawCrossSection.length; ++i) {
+            List<Point2D> points = null;
+
+            if (drawCrossSection[i]) {
+                CrossSection cs = crossSections.get(i);
+                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
+                    if (csl.getA() == cs) {
+                        points = csl.getB().fetchCrossSectionLinesPoints();
+                        generateProfile(
+                            points,
+                            cs.getDescription(),
+                            collection);
+                        break;
+                    }
+                }
+            }
+
+            if (drawWaterLevel[i]) {
+                CrossSection cs = crossSections.get(i);
+                for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
+                    if (csl.getA() == cs) {
+                        if (points != null) {
+                            points = csl.getB().fetchCrossSectionLinesPoints();
+                        }
+                        generateWaterLevels(
+                            points,
+                            collection);
+                        break;
+                    }
+                }
+            }
+
+        }
+
+        return collection;
+    }
+
+    protected Object [] createCrossSectionLineItems(
+        Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines
+    ) {
+        Object [] result = new Object[km2lines.size()];
+        int i = 0;
+        for (Map.Entry<Double, List<Pair<CrossSection, CrossSectionLine>>> entry:
+            km2lines.entrySet()) {
+            result[i++] = new CrossSectionLineItem(
+                entry.getKey(),
+                entry.getValue());
+        }
+        return result;
+    }
+
+
+    public static JFreeChart createChart(XYSeriesCollection collection) {
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            null,
+            "Abstand [m]",
+            "H\u00f6he [m]",
+            collection,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        XYPlot plot = chart.getXYPlot();
+        NumberAxis yAxis = (NumberAxis)plot.getRangeAxis();
+        yAxis.setAutoRangeIncludesZero(false);
+
+        ChartUtilities.applyCurrentTheme(chart);
+        return chart;
+    }
+
+    public static void main(String [] args) {
+        CrossSectionApp csa = new CrossSectionApp("Querprofile");
+        csa.pack();
+        RefineryUtilities.centerFrameOnScreen(csa);
+        csa.setVisible(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/context/FLYSContext.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,65 @@
+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 StateEngine in the context. */
+    public static final String ARTIFACT_KEY =
+        "artifact";
+
+    /** 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 key that is used to store a map of WMS urls for each river. */
+    public static final String RIVER_WMS =
+        "flys.floodmap.river.wms";
+
+
+    /**
+     * 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:51 2012 +0200
@@ -0,0 +1,412 @@
+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.GlobalContext;
+
+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;
+import de.intevation.flys.themes.ThemeMapping;
+
+
+/**
+ * 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";
+
+    public static final String XPATH_RIVER_WMS =
+        "/artifact-database/floodmap/river";
+
+    /**
+     * Creates a new FLYSArtifactContext object and initialize all
+     * components required by the application.
+     *
+     * @param config The artifact server configuration.
+     * @return a FLYSArtifactContext.
+     */
+    public GlobalContext createArtifactContext(Document config) {
+        FLYSContext context = new FLYSContext(config);
+
+        configureTransitions(config, context);
+        configureStates(config, context);
+        configureOutGenerators(config, context);
+        configureThemes(config, context);
+        configureThemesMappings(config, context);
+        configureRiverWMS(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, List<ThemeMapping>> mapping =
+            new HashMap<String, List<ThemeMapping>>();
+
+        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);
+
+            String pattern = (String) XMLUtils.xpath(
+                node, "@pattern", XPathConstants.STRING);
+
+            String masterAttrPattern = (String) XMLUtils.xpath(
+                node, "@masterAttr", XPathConstants.STRING);
+
+            String outputPattern = (String) XMLUtils.xpath(
+                node, "@output", XPathConstants.STRING);
+
+            if (from != null && to != null) {
+                List<ThemeMapping> tm = mapping.get(from);
+
+                if (tm == null) {
+                    tm = new ArrayList<ThemeMapping>();
+                    mapping.put(from, tm);
+                }
+
+                tm.add(new ThemeMapping(
+                    from, to, pattern, masterAttrPattern, outputPattern));
+            }
+        }
+
+        logger.debug("Found " + mapping.size() + " theme mappings.");
+
+        context.put(FLYSContext.THEME_MAPPING, mapping);
+    }
+
+
+    protected void configureRiverWMS(Document cfg, FLYSContext context) {
+        Map<String, String> riverWMS = new HashMap<String, String>();
+
+        NodeList rivers = (NodeList) XMLUtils.xpath(
+            cfg, XPATH_RIVER_WMS, XPathConstants.NODESET);
+
+        int num = rivers != null ? rivers.getLength() : 0;
+
+        for (int i = 0; i < num; i++) {
+            Element e = (Element) rivers.item(i);
+
+            String river = e.getAttribute("name");
+            String url   = XMLUtils.xpathString(e, "river-wms/@url", null);
+
+            if (river != null && url != null) {
+                riverWMS.put(river, url);
+            }
+        }
+
+        logger.debug("Found " + riverWMS.size() + " river WMS.");
+
+        context.put(FLYSContext.RIVER_WMS, riverWMS);
+    }
+}
+// vim: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:51 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/datacage/DBConfig.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,108 @@
+package de.intevation.flys.artifacts.datacage;
+
+import de.intevation.artifacts.common.utils.Config;
+
+import de.intevation.artifactdatabase.db.SQL;
+import de.intevation.artifactdatabase.db.DBConnection;
+
+import org.apache.log4j.Logger;
+
+public class DBConfig
+{ 
+    private static Logger logger = Logger.getLogger(DBConfig.class);
+
+     /**
+     * XPath to access the database driver within the global configuration.
+     */
+    public static final String DB_DRIVER =
+        "/artifact-database/datacage/driver/text()";
+    /**
+     * XPath to access the database URL within the global configuration.
+     */
+    public static final String DB_URL =
+        "/artifact-database/datacage/url/text()";
+    /**
+     * XPath to access the database use within the global configuration.
+     */
+    public static final String DB_USER =
+        "/artifact-database/datacage/user/text()";
+    /**
+     * XPath to access the database password within the global configuration.
+     */
+    public static final String DB_PASSWORD =
+        "/artifact-database/datacage/password/text()";
+
+    /**
+     * The default database driver: H2
+     */
+    public static final String DEFAULT_DRIVER =
+        "org.h2.Driver";
+
+    /**
+     * The default database user: ""
+     */
+    public static final String DEFAULT_USER = "";
+
+    /**
+     * The default database password: ""
+     */
+    public static final String DEFAULT_PASSWORD = "";
+
+
+    public static final String DEFAULT_URL =
+        "jdbc:h2:mem:datacage;INIT=RUNSCRIPT FROM '${artifacts.config.dir}/datacage.sql'";
+
+    public static final String RESOURCE_PATH = "/datacage-sql";
+
+    private static DBConfig instance;
+
+    protected DBConnection dbConnection;
+    protected SQL          sql;
+
+    public DBConfig() {
+    }
+
+    public DBConfig(DBConnection dbConnection, SQL sql) {
+        this.dbConnection = dbConnection;
+        this.sql          = sql;
+    }
+
+    public static synchronized DBConfig getInstance() {
+        if (instance == null) {
+            instance = createInstance();
+        }
+        return instance;
+    }
+
+    protected static DBConfig createInstance() {
+        String driver = Config.getStringXPath(
+            DB_DRIVER, DEFAULT_DRIVER);
+
+        String url = Config.getStringXPath(
+            DB_URL, DEFAULT_URL);
+
+        url = Config.replaceConfigDir(url);
+
+        String user = Config.getStringXPath(
+            DB_USER, DEFAULT_USER);
+
+        String password = Config.getStringXPath(
+            DB_PASSWORD, DEFAULT_PASSWORD);
+
+        DBConnection dbConnection = new DBConnection(
+            driver, url, user, password);
+
+        SQL sql = new SQL(DBConfig.class, RESOURCE_PATH, driver);
+
+        return new DBConfig(dbConnection, sql);
+    }
+
+    public DBConnection getDBConnection() {
+        return dbConnection;
+    }
+
+    public SQL getSQL() {
+        return sql;
+    }
+}
+// vim: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/datacage/Datacage.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,1074 @@
+package de.intevation.flys.artifacts.datacage;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Date;
+
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Types;
+import java.sql.Timestamp;
+
+import de.intevation.artifacts.GlobalContext;
+import de.intevation.artifacts.ArtifactCollection;
+import de.intevation.artifacts.User;
+
+import de.intevation.artifactdatabase.db.SQL;
+import de.intevation.artifactdatabase.db.SQLExecutor;
+
+import de.intevation.artifactdatabase.LifetimeListener;
+import de.intevation.artifactdatabase.Backend;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.artifactdatabase.state.Output;
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.artifacts.common.utils.LRUCache;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+public class Datacage
+implements   LifetimeListener
+{
+    private static Logger log = Logger.getLogger(Datacage.class);
+
+    public static final String DATACAGE_KEY =
+        "global.datacage.instance";
+
+    public static final String ARTEFACT_DATABASE_KEY =
+        "global.artifact.database";
+
+    private String SQL_DELETE_ALL_USERS      = "delete.all.users";
+    private String SQL_DELETE_ALL_ARTIFACTS  = "delete.all.artifacts";
+    private String SQL_USER_ID_NEXTVAL       = "user.id.nextval";
+    private String SQL_USER_BY_GID           = "user.by.gid";
+    private String SQL_INSERT_USER           = "insert.user";
+    private String SQL_COLLECTION_BY_GID     = "collection.by.gid";
+    private String SQL_COLLECTION_ID_NEXTVAL = "collection.id.nextval";
+    private String SQL_INSERT_COLLECTION     = "insert.collection";
+    private String SQL_ARTIFACT_BY_GID       = "artifact.by.gid";
+    private String SQL_COLLECTION_ITEM_ID_NEXTVAL =
+        "collection.item.id.nextval";
+    private String SQL_INSERT_COLLECTION_ITEM = "insert.collection.item";
+    private String SQL_ARTIFACT_ID_NEXTVAL    = "artifact.id.nextval";
+    private String SQL_INSERT_ARTIFACT        = "insert.artifact";
+    private String SQL_ARTIFACT_DATA_ID_NEXTVAL = "artifact.data.id.nextval";
+    private String SQL_INSERT_ARTIFACT_DATA   = "insert.artifact.data";
+    private String SQL_OUT_ID_NEXTVALUE       = "out.id.nextval";
+    private String SQL_INSERT_OUT             = "insert.out";
+    private String SQL_FACET_ID_NEXTVAL       = "facet.id.nextval";
+    private String SQL_INSERT_FACET           = "insert.facet";
+    private String SQL_UPDATE_COLLECTION_NAME = "update.collection.name";
+    private String SQL_DELETE_ARTIFACT_FROM_COLLECTION =
+        "delete.artifact.from.collection";
+    private String SQL_DELETE_COLLECTION_BY_GID =
+        "delete.collection.by.gid";
+    private String SQL_DELETE_USER_BY_GID = "delete.user.by.gid";
+    private String SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID =
+        "delete.artifact.data.by.artifact.id";
+    private String SQL_DELETE_OUTS_BY_ARTIFACT_ID =
+        "delete.outs.by.artifact.id";
+    private String SQL_DELETE_FACETS_BY_ARTIFACT_ID =
+        "delete.facets.by.artifact.id";
+    private String SQL_DELETE_ARTIFACT_BY_GID =
+        "delete.artifact.by.gid";
+
+    protected SQLExecutor sqlExecutor;
+
+    public class InitialScan 
+    implements   ArtifactDatabase.ArtifactLoadedCallback
+    {
+        protected LRUCache<String, Integer> users;
+        protected LRUCache<String, Integer> collections;
+        protected LRUCache<String, Integer> artifacts;
+
+        protected GlobalContext context;
+
+        public InitialScan() {
+            users       = new LRUCache<String, Integer>();
+            collections = new LRUCache<String, Integer>();
+            artifacts   = new LRUCache<String, Integer>();
+        }
+
+        public InitialScan(GlobalContext context) {
+            this();
+            this.context = context;
+        }
+
+        @Override
+        public void artifactLoaded(
+            String   userId,
+            String   collectionId,
+            String   collectionName,
+            Date     collectionCreated,
+            String   artifactId,
+            Date     artifactCreated,
+            Artifact artifact
+        ) {
+            if (!(artifact instanceof FLYSArtifact)) {
+                log.warn("ignoring none FLYS artifacts");
+                return;
+            }
+
+            FLYSArtifact flysArtifact = (FLYSArtifact)artifact;
+
+            Integer uId = getUserId(userId);
+            Integer cId = getCollectionId(
+                collectionId, uId, collectionName, collectionCreated);
+
+            storeArtifact(artifactId, cId, flysArtifact, artifactCreated);
+        }
+
+        protected Integer getId(
+            LRUCache<String, Integer> cache,
+            final String              idString,
+            final String              selectById
+        ) {
+            Integer id = cache.get(idString);
+            if (id != null) {
+                return id;
+            }
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(selectById);
+                    stmnt.setString(1, idString);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    return true;
+                }
+            };
+
+            if (exec.runRead()) {
+                cache.put(idString, res[0]);
+                return res[0];
+            }
+
+            return null;
+        }
+
+        protected void storeArtifact(
+            final String       artifactId,
+            Integer            collectionId,
+            final FLYSArtifact artifact,
+            final Date         artifactCreated
+        ) {
+            Integer aId = getId(artifacts, artifactId, SQL_ARTIFACT_BY_GID);
+
+            if (aId != null) {
+                // We've already stored it. Just create the collection item.
+                storeCollectionItem(collectionId, aId);
+                return;
+            }
+            // We need to write it to database
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_ARTIFACT_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_ARTIFACT);
+                    stmnt.setInt   (1, res[0]);
+                    stmnt.setString(2, artifactId);
+                    stmnt.setString(3, artifact.getCurrentStateId());
+                    Timestamp timestamp = new Timestamp(artifactCreated != null
+                        ? artifactCreated.getTime()
+                        : System.currentTimeMillis());
+                    stmnt.setTimestamp(4, timestamp);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (!exec.runWrite()) {
+                log.error("storing of artifact failed.");
+                return;
+            }
+
+            artifacts.put(artifactId, aId = res[0]);
+
+            storeCollectionItem(collectionId, aId);
+
+            storeData(aId, artifact);
+
+            storeOuts(aId, artifact, context);
+        }
+
+
+        protected void storeCollectionItem(
+            final Integer collectionId,
+            final Integer artifactId
+        ) {
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    int ciId = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_COLLECTION_ITEM);
+                    stmnt.setInt(1, ciId);
+                    stmnt.setInt(2, collectionId);
+                    stmnt.setInt(3, artifactId);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (!exec.runWrite()) {
+                log.error("storing of collection item failed.");
+            }
+        }
+
+        protected Integer getCollectionId(
+            final String  collectionId,
+            final Integer ownerId,
+            final String  collectionName,
+            final Date    collectionCreated
+        ) {
+            Integer c = getId(collections, collectionId, SQL_COLLECTION_BY_GID);
+
+            if (c != null) {
+                return c;
+            }
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_COLLECTION_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_COLLECTION);
+                    stmnt.setInt   (1, res[0]);
+                    stmnt.setString(2, collectionId);
+                    stmnt.setInt   (3, ownerId);
+                    setString(stmnt, 4, collectionName);
+                    Timestamp timestamp = new Timestamp(collectionCreated != null
+                        ? collectionCreated.getTime()
+                        : System.currentTimeMillis());
+                    stmnt.setTimestamp(5, timestamp);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (exec.runWrite()) {
+                collections.put(collectionId, res[0]);
+                return res[0];
+            }
+
+            return null;
+        }
+
+        protected Integer getUserId(final String userId) {
+
+            Integer u = getId(users, userId, SQL_USER_BY_GID);
+
+            if (u != null) {
+                return u;
+            }
+
+            final Integer [] res = new Integer[1];
+
+            SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+                @Override
+                public boolean doIt() throws SQLException {
+                    prepareStatement(SQL_USER_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        return false;
+                    }
+                    res[0] = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_USER);
+                    stmnt.setInt   (1, res[0]);
+                    stmnt.setString(2, userId);
+                    stmnt.execute();
+                    conn.commit();
+                    return true;
+                }
+            };
+
+            if (exec.runWrite()) {
+                users.put(userId, res[0]);
+                return res[0];
+            }
+
+            return null;
+        }
+
+        public boolean scan(ArtifactDatabase adb) {
+            log.debug("scan");
+            try {
+                adb.loadAllArtifacts(this);
+            }
+            catch (ArtifactDatabaseException ade) {
+                log.error(ade);
+                return false;
+            }
+            return true;
+        }
+    } // class InitialScan
+
+
+    public Datacage() {
+    }
+
+    @Override
+    public void setup(Document document) {
+        log.debug("setup");
+        DBConfig config = DBConfig.getInstance();
+        setupSQL(config.getSQL());
+        sqlExecutor = new SQLExecutor(config.getDBConnection());
+    }
+
+    protected void setupSQL(SQL sql) {
+        SQL_DELETE_ALL_USERS      = sql.get(SQL_DELETE_ALL_USERS);
+        SQL_DELETE_ALL_ARTIFACTS  = sql.get(SQL_DELETE_ALL_ARTIFACTS);
+        SQL_USER_ID_NEXTVAL       = sql.get(SQL_USER_ID_NEXTVAL);
+        SQL_USER_BY_GID           = sql.get(SQL_USER_BY_GID);
+        SQL_INSERT_USER           = sql.get(SQL_INSERT_USER);
+        SQL_COLLECTION_BY_GID     = sql.get(SQL_COLLECTION_BY_GID);
+        SQL_COLLECTION_ID_NEXTVAL = sql.get(SQL_COLLECTION_ID_NEXTVAL);
+        SQL_INSERT_COLLECTION     = sql.get(SQL_INSERT_COLLECTION);
+        SQL_ARTIFACT_BY_GID       = sql.get(SQL_ARTIFACT_BY_GID);
+        SQL_COLLECTION_ITEM_ID_NEXTVAL =
+            sql.get(SQL_COLLECTION_ITEM_ID_NEXTVAL);
+        SQL_INSERT_COLLECTION_ITEM =
+            sql.get(SQL_INSERT_COLLECTION_ITEM);
+        SQL_ARTIFACT_ID_NEXTVAL = sql.get(SQL_ARTIFACT_ID_NEXTVAL);
+        SQL_INSERT_ARTIFACT     = sql.get(SQL_INSERT_ARTIFACT);
+        SQL_ARTIFACT_DATA_ID_NEXTVAL = sql.get(SQL_ARTIFACT_DATA_ID_NEXTVAL);
+        SQL_INSERT_ARTIFACT_DATA = sql.get(SQL_INSERT_ARTIFACT_DATA);
+        SQL_OUT_ID_NEXTVALUE     = sql.get(SQL_OUT_ID_NEXTVALUE);
+        SQL_INSERT_OUT           = sql.get(SQL_INSERT_OUT);
+        SQL_FACET_ID_NEXTVAL     = sql.get(SQL_FACET_ID_NEXTVAL);
+        SQL_INSERT_FACET         = sql.get(SQL_INSERT_FACET);
+        SQL_UPDATE_COLLECTION_NAME = sql.get(SQL_UPDATE_COLLECTION_NAME);
+        SQL_DELETE_ARTIFACT_FROM_COLLECTION =
+            sql.get(SQL_DELETE_ARTIFACT_FROM_COLLECTION);
+        SQL_DELETE_COLLECTION_BY_GID = sql.get(SQL_DELETE_COLLECTION_BY_GID);
+        SQL_DELETE_USER_BY_GID       = sql.get(SQL_DELETE_USER_BY_GID);
+        SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID =
+            sql.get(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID);
+        SQL_DELETE_OUTS_BY_ARTIFACT_ID =
+            sql.get(SQL_DELETE_OUTS_BY_ARTIFACT_ID);
+        SQL_DELETE_FACETS_BY_ARTIFACT_ID =
+            sql.get(SQL_DELETE_FACETS_BY_ARTIFACT_ID);
+        SQL_DELETE_ARTIFACT_BY_GID =
+            sql.get(SQL_DELETE_ARTIFACT_BY_GID);
+    }
+
+    protected static final int numFacets(List<Output> outs) {
+        int sum = 0;
+        for (Output out: outs) {
+            sum += out.getFacets().size();
+        }
+        return sum;
+    }
+
+    protected static final void setString(
+        PreparedStatement stmnt, 
+        int               index,
+        Object            value
+    ) 
+    throws SQLException
+    {
+        if (value == null) {
+            stmnt.setNull(index, Types.VARCHAR);
+        }
+        else {
+            stmnt.setString(index, value.toString());
+        }
+    }
+
+    @Override
+    public void systemUp(GlobalContext context) {
+        log.debug("systemUp entered");
+        initialScan(context);
+        context.put(DATACAGE_KEY, this);
+        log.debug("systemUp leaved");
+    }
+
+    protected void initialScan(GlobalContext context) {
+        log.debug("initialScan");
+
+        Object adbObject = context.get(ARTEFACT_DATABASE_KEY);
+
+        if (!(adbObject instanceof ArtifactDatabase)) {
+            log.error("missing artefact database. Cannot scan");
+            return;
+        }
+
+        ArtifactDatabase adb = (ArtifactDatabase)adbObject;
+
+        if (!cleanDatabase()) {
+            log.error("cleaning database failed");
+            return;
+        }
+
+        InitialScan is = new InitialScan(context);
+
+        if (!is.scan(adb)) {
+            log.error("initial scan failed");
+            return;
+        }
+
+    }
+
+    protected boolean cleanDatabase() {
+
+        log.debug("cleanDatabase");
+
+        boolean success = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_ALL_USERS);
+                stmnt.execute();
+                prepareStatement(SQL_DELETE_ALL_ARTIFACTS);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        }.runWrite();
+
+        log.debug("after runWrite(): " + success);
+
+        return success;
+    }
+
+
+    @Override
+    public void systemDown(GlobalContext context) {
+        log.debug("systemDown");
+    }
+
+    public void setup(GlobalContext globalContext) {
+        log.debug("setup");
+    }
+
+    public void createdArtifact(
+        Artifact      artifact, 
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("createdArtifact");
+
+        if (artifact == null) {
+            log.warn("artifact to create is null");
+            return;
+        }
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            log.warn("need FLYSArtifact here (have " + artifact.getClass() + ")");
+            return;
+        }
+
+        final FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        final int [] res = new int[1];
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    log.error("id generation for artifact failed");
+                    return false;
+                }
+                res[0] = result.getInt(1);
+                reset();
+                prepareStatement(SQL_INSERT_ARTIFACT);
+                stmnt.setInt      (1, res[0]);
+                stmnt.setString   (2, flys.identifier());
+                stmnt.setString   (3, flys.getCurrentStateId());
+                stmnt.setTimestamp(4,
+                    new Timestamp(System.currentTimeMillis()));
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing of artifact failed.");
+            return;
+        }
+
+        storeData(res[0], flys);
+        storeOuts(res[0], flys, context);
+    }
+
+    public void storedArtifact(
+        Artifact      artifact,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("storedArtifact");
+        if (!(artifact instanceof FLYSArtifact)) {
+            log.warn("need FLYSArtifact here but have a " + artifact.getClass());
+            return;
+        }
+
+        final FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        final Integer [] res = new Integer[1];
+
+        // check first if artifact already exists
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_BY_GID);
+                stmnt.setString(1, flys.identifier());
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    // new artifact
+                    return true;
+                }
+                res[0] = result.getInt(1);
+                return true;
+            }
+        };
+
+        if (!exec.runRead()) {
+            log.error("querying artifact failed");
+            return;
+        }
+
+        if (res[0] == null) { // new artifact
+            createdArtifact(artifact, backend, context);
+            return;
+        }
+
+        // artifact already exists -> delete old data
+        exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID);
+                stmnt.setInt(1, res[0]);
+                stmnt.execute();
+                prepareStatement(SQL_DELETE_FACETS_BY_ARTIFACT_ID);
+                stmnt.setInt(1, res[0]);
+                stmnt.execute();
+                prepareStatement(SQL_DELETE_OUTS_BY_ARTIFACT_ID);
+                stmnt.setInt(1, res[0]);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("deleting old artifact data failed");
+            return;
+        }
+
+        // write new data
+        storeData(res[0], flys);
+        storeOuts(res[0], flys, context);
+    }
+
+    public void createdUser(
+        final User    user,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("createdUser");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_USER_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    log.error("id generation for user failed");
+                    return false;
+                }
+                int uId = result.getInt(1);
+                reset();
+                prepareStatement(SQL_INSERT_USER);
+                stmnt.setInt(1, uId);
+                stmnt.setString(2, user.identifier());
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("create user failed");
+        }
+    }
+
+    public void deletedUser(
+        final String  identifier,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("deletedUser");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_USER_BY_GID);
+                stmnt.setString(1, identifier);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("delete user failed");
+        }
+    }
+
+    public void createdCollection(
+        final ArtifactCollection collection,
+        Backend                  backend,
+        GlobalContext            context
+    ) {
+        log.debug("createdCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                String userId = collection.getUser().identifier();
+                prepareStatement(SQL_USER_BY_GID);
+                stmnt.setString(1, userId);
+                result = stmnt.executeQuery();
+                int uId;
+                if (result.next()) {
+                    uId = result.getInt(1);
+                    reset();
+                }
+                else {
+                    // need to create user first
+                    reset();
+                    prepareStatement(SQL_USER_ID_NEXTVAL);
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("id generation for user failed");
+                        return false;
+                    }
+                    uId = result.getInt(1);
+                    reset();
+                    prepareStatement(SQL_INSERT_USER);
+                    stmnt.setInt(1, uId);
+                    stmnt.setString(2, userId);
+                    stmnt.execute();
+                    conn.commit();
+                    reset();
+                }
+
+                prepareStatement(SQL_COLLECTION_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    log.error("id generation for collection failed");
+                    return false;
+                }
+                int cId = result.getInt(1);
+                reset();
+
+                String identifier = collection.identifier();
+                String name       = collection.getName();
+
+                prepareStatement(SQL_INSERT_COLLECTION);
+                stmnt.setInt(1, cId);
+                stmnt.setString(2, identifier);
+                stmnt.setInt(3, uId);
+                setString(stmnt, 4, name);
+                stmnt.setTimestamp(5,
+                    new Timestamp(System.currentTimeMillis()));
+                stmnt.execute();
+
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("create collection failed");
+        }
+    }
+
+    public void deletedCollection(
+        final String  identifier,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("deletedCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_COLLECTION_BY_GID);
+                stmnt.setString(1, identifier);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("delete collection failed");
+        }
+    }
+
+    public void changedCollectionAttribute(
+        String        identifier,
+        Document      document,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("changedCollectionAttribute");
+    }
+
+    public void changedCollectionItemAttribute(
+        String        collectionId,
+        String        artifactId,
+        Document      document,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("changedCollectionItemAttribute");
+    }
+
+    public void addedArtifactToCollection(
+        final String  artifactId,
+        final String  collectionId,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("addedArtifactToCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_BY_GID);
+                stmnt.setString(1, artifactId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int aId = result.getInt(1);
+                reset();
+
+                prepareStatement(SQL_COLLECTION_BY_GID);
+                stmnt.setString(1, collectionId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int cId = result.getInt(1);
+                reset();
+
+                prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int ciId = result.getInt(1);
+                reset();
+
+                prepareStatement(SQL_INSERT_COLLECTION_ITEM);
+                stmnt.setInt(1, ciId);
+                stmnt.setInt(2, cId);
+                stmnt.setInt(3, aId);
+                stmnt.execute();
+
+                conn.commit();
+                return true;
+            }
+        };
+        if (!exec.runWrite()) {
+            log.error("added artifact to collection failed");
+        }
+    }
+
+    public void removedArtifactFromCollection(
+        final String  artifactId,
+        final String  collectionId,
+        Backend       backend,
+        GlobalContext context
+    ) {
+        log.debug("removedArtifactFromCollection");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_ARTIFACT_BY_GID);
+                stmnt.setString(1, artifactId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int aId = result.getInt(1);
+                reset();
+                prepareStatement(SQL_COLLECTION_BY_GID);
+                stmnt.setString(1, collectionId);
+                result = stmnt.executeQuery();
+                if (!result.next()) {
+                    return false;
+                }
+                int cId = result.getInt(1);
+                reset();
+                prepareStatement(SQL_DELETE_ARTIFACT_FROM_COLLECTION);
+                stmnt.setInt(1, cId);
+                stmnt.setInt(2, aId);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+        if (!exec.runWrite()) {
+            log.error("removing artifact from collection failed");
+        }
+    }
+
+    public void setCollectionName(
+        final String  collectionId,
+        final String  name,
+        GlobalContext context
+    ) {
+        log.debug("setCollectionName");
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_UPDATE_COLLECTION_NAME);
+                stmnt.setString(1, name);
+                stmnt.setString(2, collectionId);
+                stmnt.execute();
+                conn.commit();
+                return true;
+            }
+        };
+        if (!exec.runWrite()) {
+            log.error("changing name failed");
+        }
+    }
+
+    protected void storeData(
+        final int     artifactId,
+        FLYSArtifact  artifact
+    ) {
+        final Collection<StateData> data = artifact.getAllData();
+
+        if (data.isEmpty()) {
+            return;
+        }
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                int [] ids = new int[data.size()];
+                prepareStatement(SQL_ARTIFACT_DATA_ID_NEXTVAL);
+
+                for (int i = 0; i < ids.length; ++i) {
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("generating id for artifact data failed");
+                        return false;
+                    }
+                    ids[i] = result.getInt(1);
+                    result.close(); result = null;
+                }
+                reset();
+                prepareStatement(SQL_INSERT_ARTIFACT_DATA);
+
+                int i = 0;
+                for (StateData sd: data) {
+                    int id = ids[i++];
+                    stmnt.setInt(1, id);
+                    stmnt.setInt(2, artifactId);
+                    // XXX: Where come the nulls from?
+                    String type = sd.getType();
+                    if (type == null) type = "String";
+                    stmnt.setString(3, type);
+                    stmnt.setString(4, sd.getName());
+                    setString(stmnt, 5, sd.getValue());
+                    stmnt.execute();
+                }
+
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing artifact data failed");
+        }
+    }
+
+    protected void storeOuts(
+        final int          artifactId,
+        final FLYSArtifact artifact,
+        GlobalContext      context
+    ) {
+        final List<Output> outs = artifact.getOutputs(context);
+
+        if (outs.isEmpty()) {
+            return;
+        }
+
+        final int [] outIds = new int[outs.size()];
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_OUT_ID_NEXTVALUE);
+                for (int i = 0; i < outIds.length; ++i) {
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("generation of out ids failed");
+                        return false;
+                    }
+                    outIds[i] = result.getInt(1);
+                    result.close(); result = null;
+                }
+                reset();
+                prepareStatement(SQL_INSERT_OUT);
+                for (int i = 0; i < outIds.length; ++i) {
+                    Output out = outs.get(i);
+                    stmnt.setInt(1, outIds[i]);
+                    stmnt.setInt(2, artifactId);
+                    stmnt.setString(3, out.getName());
+                    setString(stmnt, 4, out.getDescription());
+                    setString(stmnt, 5, out.getType());
+                    stmnt.execute();
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing artifact outs failed");
+            return;
+        }
+
+        final int FACETS = numFacets(outs);
+
+        if (FACETS == 0) {
+            return;
+        }
+
+        exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                int [] facetIds = new int[FACETS];
+                prepareStatement(SQL_FACET_ID_NEXTVAL);
+                for (int i = 0; i < facetIds.length; ++i) {
+                    result = stmnt.executeQuery();
+                    if (!result.next()) {
+                        log.error("generation of facet ids failed");
+                        return false;
+                    }
+                    facetIds[i] = result.getInt(1);
+                    result.close(); result = null;
+                }
+                reset();
+                prepareStatement(SQL_INSERT_FACET);
+                int index = 0;
+                for (int i = 0, N = outs.size(); i < N; ++i) {
+                    Output out = outs.get(i);
+                    int outId = outIds[i];
+                    for (Facet facet: out.getFacets()) {
+                        stmnt.setInt(1, facetIds[index]);
+                        stmnt.setInt(2, outId);
+                        stmnt.setString(3, facet.getName());
+                        stmnt.setInt(4, facet.getIndex());
+                        stmnt.setString(5, "XXX"); // TODO: handle states
+                        setString(stmnt, 6, facet.getDescription());
+                        stmnt.execute();
+                        ++index;
+                    }
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("storing facets failed");
+        }
+    }
+
+    public void killedCollections(
+        final List<String> identifiers,
+        GlobalContext      context
+    ) {
+        log.debug("killedCollections");
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_COLLECTION_BY_GID);
+                for (String identifier: identifiers) {
+                    stmnt.setString(1, identifier);
+                    stmnt.execute();
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("killing collections failed");
+        }
+    }
+
+    public void killedArtifacts(
+        final List<String> identifiers,
+        GlobalContext      context
+    ) {
+        log.debug("killedArtifacts");
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
+            @Override
+            public boolean doIt() throws SQLException {
+                prepareStatement(SQL_DELETE_ARTIFACT_BY_GID);
+                for (String identifier: identifiers) {
+                    stmnt.setString(1, identifier);
+                    stmnt.execute();
+                }
+                conn.commit();
+                return true;
+            }
+        };
+
+        if (!exec.runWrite()) {
+            log.error("killing artifacts failed");
+        }
+    }
+}
+// vim: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/datacage/DatacageBackendListener.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,190 @@
+package de.intevation.flys.artifacts.datacage;
+
+import java.util.List;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactCollection;
+import de.intevation.artifacts.GlobalContext;
+import de.intevation.artifacts.User;
+
+import de.intevation.artifactdatabase.BackendListener;
+import de.intevation.artifactdatabase.Backend;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+public class DatacageBackendListener
+implements   BackendListener
+{
+    private static Logger log =
+        Logger.getLogger(DatacageBackendListener.class);
+
+    protected GlobalContext context;
+
+    public DatacageBackendListener() {
+        log.debug("new DatacageBackendListener");
+    }
+
+    protected Datacage getDatacage() {
+        Object listener = context.get(Datacage.DATACAGE_KEY);
+        return listener instanceof Datacage
+            ? (Datacage)listener
+            : null;
+    }
+
+    @Override
+    public void setup(GlobalContext context) {
+        log.debug("setup");
+        this.context = context;
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.setup(context);
+        }
+    }
+
+    @Override
+    public void createdArtifact(Artifact artifact, Backend backend) {
+        log.debug("createdArtifact");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.createdArtifact(artifact, backend, context);
+        }
+    }
+
+    @Override
+    public void storedArtifact(Artifact artifact, Backend backend) {
+        log.debug("storedArtifact");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.storedArtifact(artifact, backend, context);
+        }
+    }
+
+    @Override
+    public void createdUser(User user, Backend backend) {
+        log.debug("createdUser");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.createdUser(user, backend, context);
+        }
+    }
+
+    @Override
+    public void deletedUser(String identifier, Backend backend) {
+        log.debug("deletedUser");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.deletedUser(identifier, backend, context);
+        }
+    }
+
+    @Override
+    public void createdCollection(
+        ArtifactCollection collection,
+        Backend            backend
+    ) {
+        log.debug("createdCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.createdCollection(collection, backend, context);
+        }
+    }
+
+    @Override
+    public void deletedCollection(String identifier, Backend backend) {
+        log.debug("deletedCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.deletedCollection(identifier, backend, context);
+        }
+    }
+
+    @Override
+    public void changedCollectionAttribute(
+        String   identifier,
+        Document document,
+        Backend  backend
+    ) {
+        log.debug("changedCollectionAttribute");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.changedCollectionAttribute(
+                identifier, document, backend, context);
+        }
+    }
+
+    @Override
+    public void changedCollectionItemAttribute(
+        String   collectionId,
+        String   artifactId,
+        Document document,
+        Backend  backend
+    ) {
+        log.debug("changedCollectionItemAttribute");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.changedCollectionItemAttribute(
+                collectionId, artifactId, document, backend, context);
+        }
+    }
+
+    @Override
+    public void addedArtifactToCollection(
+        String  artifactId,
+        String  collectionId,
+        Backend backend
+    ) {
+        log.debug("addedArtifactToCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.addedArtifactToCollection(
+                artifactId, collectionId, backend, context);
+        }
+    }
+
+    @Override
+    public void removedArtifactFromCollection(
+        String  artifactId,
+        String  collectionId,
+        Backend backend
+    ) {
+        log.debug("removedArtifactFromCollection");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.removedArtifactFromCollection(
+                artifactId, collectionId, backend, context);
+        }
+    }
+
+    @Override
+    public void setCollectionName(
+        String collectionId,
+        String name
+    ) {
+        log.debug("setCollectionName");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.setCollectionName(collectionId, name, context);
+        }
+    }
+
+    @Override
+    public void killedCollections(List<String> identifiers, Backend backend) {
+        log.debug("killedCollections");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.killedCollections(identifiers, context);
+        }
+    }
+
+    @Override
+    public void killedArtifacts(List<String> identifiers, Backend backend) {
+        log.debug("killedArtifacts");
+        Datacage l = getDatacage();
+        if (l != null) {
+            l.killedArtifacts(identifiers, 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/artifacts/datacage/Recommendations.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,291 @@
+package de.intevation.flys.artifacts.datacage;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+
+import java.io.FileInputStream;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.hibernate.Session;
+
+import org.hibernate.jdbc.Work;
+
+import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.StringUtils;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.datacage.templating.Builder;
+
+public class Recommendations
+{
+    private static Logger log = Logger.getLogger(Recommendations.class);
+
+    private static final boolean DEVELOPMENT_MODE =
+        Boolean.getBoolean("flys.datacage.recommendations.development");
+
+    public static final String XPATH_TEMPLATE =
+        "/artifact-database/metadata/template/text()";
+
+    public static final String DEFAULT_TEMPLATE_PATH =
+        "${artifacts.config.dir}/meta-data.xml";
+
+    private static Recommendations INSTANCE;
+
+    public static class BuilderProvider
+    {
+        protected Builder builder;
+
+        public BuilderProvider() {
+        }
+
+        public BuilderProvider(Builder builder) {
+            this.builder = builder;
+        }
+
+        public Builder getBuilder() {
+            return builder;
+        }
+    } // class BuilderProvider
+
+    public static class FileBuilderProvider
+    extends             BuilderProvider
+    {
+        protected File file;
+        protected long lastModified;
+
+        public FileBuilderProvider() {
+        }
+
+        public FileBuilderProvider(File file) {
+            this.file    = file;
+            lastModified = Long.MIN_VALUE;
+        }
+
+        @Override
+        public synchronized Builder getBuilder() {
+            long modified = file.lastModified();
+            if (modified > lastModified) {
+                lastModified = modified;
+                try {
+                    Document template = loadTemplate(file);
+                    builder = new Builder(template);
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+            return builder;
+        }
+
+        public BuilderProvider toStaticProvider() {
+            return new BuilderProvider(builder);
+        }
+    } // class BuilderProvider
+
+    protected BuilderProvider builderProvider;
+
+    public Recommendations() {
+    }
+
+    public Recommendations(BuilderProvider builderProvider) {
+        this.builderProvider = builderProvider;
+    }
+
+    public Builder getBuilder() {
+        return builderProvider.getBuilder();
+    }
+
+    protected static void artifactToParameters(
+        FLYSArtifact        artifact, 
+        Map<String, Object> parameters
+    ) {
+        parameters.put("CURRENT-STATE-ID", artifact.getCurrentStateId());
+        parameters.put("ARTIFACT-ID",      artifact.identifier());
+
+        for (StateData sd: artifact.getAllData()) {
+            Object value = sd.getValue();
+            if (value == null) {
+                continue;
+            }
+            String key = sd.getName().replace('.', '-').toUpperCase();
+            parameters.put(key, value);
+        }
+    }
+
+    public static void convertKeysToUpperCase(
+        Map<String, Object> src,
+        Map<String, Object> dst
+    ) {
+        for (Map.Entry<String, Object> entry: src.entrySet()) {
+            dst.put(entry.getKey().toUpperCase(), entry.getValue());
+        }
+    }
+
+    public void  recommend(
+        FLYSArtifact        artifact,
+        String              userId,
+        String []           outs,
+        Map<String, Object> extraParameters,
+        Node                result
+    ) {
+        Map<String, Object> parameters = new HashMap<String, Object>();
+
+        if (extraParameters != null) {
+            convertKeysToUpperCase(extraParameters, parameters);
+        }
+
+        if (userId != null) {
+            parameters.put("USER-ID", userId);
+        }
+
+        if (artifact != null) {
+            artifactToParameters(artifact, parameters);
+        }
+
+        parameters.put("ARTIFACT-OUTS", StringUtils.toUpperCase(outs));
+
+        parameters.put("PARAMETERS", parameters);
+
+        recommend(parameters, userId, result);
+    }
+
+    public void recommend(
+        Map<String, Object> parameters,
+        String              userId,
+        Node                result
+    ) {
+        recommend(parameters, userId, result, SessionHolder.HOLDER.get());
+    }
+
+    public void recommend(
+        final Map<String, Object> parameters,
+        final String              userId,
+        final Node                result,
+        Session                   session
+    ) {
+        session.doWork(new Work() {
+            @Override
+            public void execute(Connection systemConnection)
+            throws SQLException
+            {
+                List<Builder.NamedConnection> connections =
+                    new ArrayList<Builder.NamedConnection>(2);
+
+                Connection userConnection = userId != null
+                    ? DBConfig
+                        .getInstance()
+                        .getDBConnection()
+                        .getDataSource()
+                        .getConnection()
+                    : null;
+
+                try {
+                    if (userConnection != null) {
+                        connections.add(new Builder.NamedConnection(
+                            Builder.CONNECTION_USER, userConnection, false));
+                    }
+
+                    connections.add(new Builder.NamedConnection(
+                        Builder.CONNECTION_SYSTEM, systemConnection, true));
+
+                    getBuilder().build(connections, result, parameters);
+                }
+                finally {
+                    if (userConnection != null) {
+                        userConnection.close();
+                    }
+                }
+            }
+        });
+    }
+
+    public static synchronized Recommendations getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = createRecommendations();
+        }
+        return INSTANCE;
+    }
+
+    protected static Document loadTemplate(File file) throws IOException {
+        InputStream in = null;
+
+        try {
+            in = new FileInputStream(file);
+
+            Document template = XMLUtils.parseDocument(in);
+
+            if (template == null) {
+                throw new IOException("cannot load template");
+            }
+            return template;
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+        }
+    }
+
+    public static Recommendations createRecommendations(File file) {
+        log.debug("Recommendations.createBuilder");
+
+        if (!file.isFile() || !file.canRead()) {
+            log.error("Cannot open template file '" + file + "'");
+            return null;
+        }
+
+        FileBuilderProvider fbp = new FileBuilderProvider(file);
+
+        if (fbp.getBuilder() == null) {
+            log.error("failed loading builder");
+            return null;
+        }
+
+        BuilderProvider bp = DEVELOPMENT_MODE
+            ? fbp
+            : fbp.toStaticProvider();
+
+        return new Recommendations(bp);
+    }
+
+    protected static Recommendations createRecommendations() {
+        log.debug("Recommendations.createRecommendations");
+
+        String path = Config.getStringXPath(XPATH_TEMPLATE);
+
+        if (path == null) {
+            path = DEFAULT_TEMPLATE_PATH;
+        }
+
+        path = Config.replaceConfigDir(path);
+
+        log.info("Meta data template: " + path);
+
+        return createRecommendations(new File(path));
+    }
+}
+// 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/datacage/templating/App.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,109 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+
+import de.intevation.flys.backend.SessionFactoryProvider;
+
+import org.hibernate.Session;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.datacage.Recommendations;
+
+public class App
+{
+    private static Logger log = Logger.getLogger(App.class);
+
+    public static final String template =
+        System.getProperty("meta.data.template", "meta-data.xml");
+
+    public static final String userId =
+        System.getProperty("user.id");
+
+    public static final String PARAMETERS =
+        System.getProperty("meta.data.parameters", "");
+
+    public static final String OUTPUT =
+        System.getProperty("meta.data.output");
+
+    public static Map<String, Object> getParameters() {
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        String [] parts = PARAMETERS.split("\\s*;\\s*");
+        for (String part: parts) {
+            String [] kv = part.split("\\s*:\\s*");
+            if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
+                continue;
+            }
+            String [] values = kv[1].split("\\s*,\\s*");
+            map.put(kv[0], values.length == 1 ? values[0] : values);
+        }
+        return map;
+    }
+
+    public static void main(String [] args) {
+
+        Recommendations rec = Recommendations.createRecommendations(
+            new File(template));
+
+        if (rec == null) {
+            System.err.println("No recommendations created");
+            return;
+        }
+
+        final Document result = XMLUtils.newDocument();
+
+        final Map<String, Object> parameters = getParameters();
+
+        Session session = SessionFactoryProvider
+            .createSessionFactory()
+            .openSession();
+
+        try {
+            rec.recommend(parameters, userId, result, session);
+        }
+        finally {
+            session.close();
+        }
+
+        OutputStream out;
+
+        if (OUTPUT == null) {
+            out = System.out;
+        }
+        else {
+            try {
+                out = new FileOutputStream(OUTPUT);
+            }
+            catch (IOException ioe) {
+                log.error(ioe);
+                return;
+            }
+        }
+
+        try {
+            XMLUtils.toStream(result, out);
+        }
+        finally {
+            if (OUTPUT != null) {
+                try {
+                    out.close();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+        }
+        System.exit(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/datacage/templating/Builder.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,605 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Deque;
+import java.util.ArrayDeque;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathConstants;
+
+import java.sql.SQLException;
+import java.sql.Connection;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.utils.Pair;
+
+import org.apache.log4j.Logger;
+
+public class Builder
+{
+    private static Logger log = Logger.getLogger(Builder.class);
+
+    public static final String CONNECTION_USER   = "user";
+    public static final String CONNECTION_SYSTEM = "system";
+    public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM;
+
+    public static final Pattern STRIP_LINE_INDENT =
+        Pattern.compile("\\s*\\r?\\n\\s*");
+
+    public static final String DC_NAMESPACE_URI =
+        "http://www.intevation.org/2011/Datacage";
+
+    private static final Document EVAL_DOCUMENT =
+        XMLUtils.newDocument();
+
+    private static final XPathFactory XPATH_FACTORY =
+        XPathFactory.newInstance();
+
+    protected Document template;
+
+    protected Map<String, CompiledStatement> compiledStatements;
+
+    public static class NamedConnection {
+
+        protected String     name;
+        protected Connection connection;
+        protected boolean    cached;
+
+        public NamedConnection() {
+        }
+
+        public NamedConnection(
+            String     name, 
+            Connection connection
+        ) {
+            this(name, connection, true);
+        }
+
+        public NamedConnection(
+            String     name, 
+            Connection connection,
+            boolean    cached
+        ) {
+            this.name       = name;
+            this.connection = connection;
+            this.cached     = cached;
+        }
+    } // class NamedConnection
+
+
+    public class BuildHelper
+    {
+        protected Node                                     output;
+        protected Document                                 owner;
+        protected StackFrames                              frames;
+        protected List<NamedConnection>                    connections;
+        protected Map<String, CompiledStatement.Instance>  statements;
+        protected Deque<Pair<NamedConnection, ResultData>> connectionsStack;
+
+        public BuildHelper(
+            Node                  output,
+            List<NamedConnection> connections,
+            Map<String, Object>   parameters
+        ) {
+            if (connections.isEmpty()) {
+                throw new IllegalArgumentException("no connections given.");
+            }
+
+            this.connections = connections;
+            connectionsStack =
+                new ArrayDeque<Pair<NamedConnection, ResultData>>();
+            this.output      = output;
+            frames           = new StackFrames(parameters);
+            owner            = getOwnerDocument(output);
+            statements =
+                new HashMap<String, CompiledStatement.Instance>();
+        }
+
+        public void build() throws SQLException {
+            try {
+                synchronized (template) {
+                    for (Node current: rootsToList()) {
+                        build(output, current);
+                    }
+                }
+            }
+            finally {
+                closeStatements();
+            }
+        }
+
+        protected void closeStatements() {
+            for (CompiledStatement.Instance csi: statements.values()) {
+                csi.close();
+            }
+            statements.clear();
+        }
+
+        protected void context(Node parent, Element current)
+        throws SQLException
+        {
+            log.debug("dc:context");
+
+            NodeList subs = current.getChildNodes();
+            int S = subs.getLength();
+
+            // check only direct children
+            Node stmntNode = null;
+            for (int i = 0; i < S; ++i) {
+                Node node = subs.item(i);
+                String ns;
+                if (node.getNodeType() == Node.ELEMENT_NODE
+                &&  node.getLocalName().equals("statement")
+                && (ns = node.getNamespaceURI()) != null
+                && ns.equals(DC_NAMESPACE_URI)) {
+                    stmntNode = node;
+                    break;
+                }
+            }
+
+            if (stmntNode == null) {
+                log.warn("dc:context: cannot find statement");
+                return;
+            }
+
+            String con = current.getAttribute("connection");
+
+            String stmntText = stmntNode.getTextContent();
+
+            String key = con + "-" + stmntText;
+
+            CompiledStatement.Instance csi = statements.get(key);
+
+            if (csi == null) {
+                CompiledStatement cs = compiledStatements.get(stmntText);
+                csi = cs.new Instance();
+                statements.put(key, csi);
+            }
+
+            NamedConnection connection = connectionsStack.isEmpty()
+                ? connections.get(0)
+                : connectionsStack.peek().getA();
+
+            if (con.length() > 0) {
+                for (NamedConnection nc: connections) {
+                    if (con.equals(nc.name)) {
+                        connection = nc;
+                        break;
+                    }
+                }
+            }
+
+            ResultData rd = csi.execute(
+                connection.connection,
+                frames,
+                connection.cached);
+
+            // only descent if there are results
+            if (!rd.isEmpty()) {
+                connectionsStack.push(new Pair(connection, rd));
+                try {
+                    for (int i = 0; i < S; ++i) {
+                        build(parent, subs.item(i));
+                    }
+                }
+                finally {
+                    connectionsStack.pop();
+                }
+            }
+        }
+
+        protected void elements(Node parent, Element current) 
+        throws SQLException
+        {
+            log.debug("dc:elements");
+
+            if (connectionsStack.isEmpty()) {
+                log.warn("dc:elements without having results");
+                return;
+            }
+
+            NodeList subs = current.getChildNodes();
+            int S = subs.getLength();
+
+            if (S == 0) {
+                log.debug("dc:elements has no children");
+                return;
+            }
+
+            ResultData rd = connectionsStack.peek().getB();
+
+            String [] columns = rd.getColumnLabels();
+
+            //if (log.isDebugEnabled()) {
+            //    log.debug("pushing vars: "
+            //        + java.util.Arrays.toString(columns));
+            //}
+
+            for (Object [] row: rd.getRows()) {
+                frames.enter();
+                try {
+                    frames.put(columns, row);
+                    //if (log.isDebugEnabled()) {
+                    //    log.debug("current vars: " + frames.dump());
+                    //}
+                    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 = owner.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(owner.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 callMacro(Node parent, Element current)
+        throws SQLException
+        {
+            String name = current.getAttribute("name");
+
+            if (name.length() == 0) {
+                log.warn("missing 'name' attribute in 'call-macro'");
+                return;
+            }
+
+            NodeList macros = template.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "macro");
+
+            for (int i = 0, N = macros.getLength(); i < N; ++i) {
+                Element macro = (Element)macros.item(i);
+                if (name.equals(macro.getAttribute("name"))) {
+                    NodeList subs = macro.getChildNodes();
+                    for (int j = 0, M = subs.getLength(); j < M; ++j) {
+                        build(parent, subs.item(j));
+                    }
+                    return;
+                }
+            }
+
+            log.warn("no macro '" + name + "' found.");
+        }
+
+        protected void ifClause(Node parent, Element current)
+        throws SQLException
+        {
+            String test = current.getAttribute("test");
+
+            if (test.length() == 0) {
+                log.warn("missing 'test' attribute in 'if'");
+                return;
+            }
+
+            Boolean result = evaluateXPath(test);
+
+            if (result != null && result.booleanValue()) {
+                NodeList subs = current.getChildNodes();
+                for (int i = 0, N = subs.getLength(); i < N; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+        }
+
+        protected void choose(Node parent, Element current)
+        throws SQLException
+        {
+            Node branch = null;
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                Node child = children.item(i);
+                String ns = child.getNamespaceURI();
+                if (ns == null
+                || !ns.equals(DC_NAMESPACE_URI)
+                || child.getNodeType() != Node.ELEMENT_NODE
+                ) {
+                    continue;
+                }
+                String name = child.getLocalName();
+                if ("when".equals(name)) {
+                    Element when = (Element)child;
+                    String test = when.getAttribute("test");
+                    if (test.length() == 0) {
+                        log.warn("no 'test' attribute found for when");
+                        continue;
+                    }
+
+                    Boolean result = evaluateXPath(test);
+                    if (result != null && result.booleanValue()) {
+                        branch = child;
+                        break;
+                    }
+
+                    continue;
+                }
+                else if ("otherwise".equals(name)) {
+                    branch = child;
+                    // No break here.
+                }
+            }
+
+            if (branch != null) {
+                NodeList subs = branch.getChildNodes();
+                for (int i = 0, N = subs.getLength(); i < N; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+        }
+
+        protected Boolean evaluateXPath(String expr) {
+
+            if (log.isDebugEnabled()) {
+                log.debug("evaluate: '" + expr + "'");
+            }
+
+            try {
+                XPath xpath = XPATH_FACTORY.newXPath();
+                xpath.setXPathVariableResolver(frames);
+                xpath.setXPathFunctionResolver(FunctionResolver.FUNCTIONS);
+                Object result = xpath.evaluate(
+                    expr, EVAL_DOCUMENT, XPathConstants.BOOLEAN);
+
+                return result instanceof Boolean
+                    ? (Boolean)result
+                    : null;
+            }
+            catch (XPathExpressionException xfce) {
+                log.error(xfce);
+            }
+            return null;
+        }
+
+        protected void convert(Node parent, Element current) {
+
+            String variable = expand(current.getAttribute("var"));
+            String type     = expand(current.getAttribute("type"));
+
+            Object [] result = new Object[1];
+
+            if (frames.getStore(variable, result)) {
+                Object object = TypeConverter.convert(result[0], type);
+                frames.put(variable.toUpperCase(), object);
+            }
+        }
+
+        protected String expand(String s) {
+            Matcher m = CompiledStatement.VAR.matcher(s);
+
+            Object [] result = new Object[1];
+
+            StringBuffer sb = new StringBuffer();
+            while (m.find()) {
+                String key = m.group(1);
+                result[0] = null;
+                if (frames.getStore(key, result)) {
+                    m.appendReplacement(
+                        sb, result[0] != null ? result[0].toString() : "");
+                }
+                else {
+                    m.appendReplacement(sb, "\\${" + key + "}");
+                }
+            }
+            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 ("attribute".equals(localName)) {
+                        attribute(parent, (Element)current);
+                    }
+                    else if ("context".equals(localName)) {
+                        context(parent, (Element)current);
+                    }
+                    else if ("if".equals(localName)) {
+                        ifClause(parent, (Element)current);
+                    }
+                    else if ("choose".equals(localName)) {
+                        choose(parent, (Element)current);
+                    }
+                    else if ("call-macro".equals(localName)) {
+                        callMacro(parent, (Element)current);
+                    }
+                    else if ("macro".equals(localName)) {
+                        // Simply ignore the definition.
+                    }
+                    else if ("element".equals(localName)) {
+                        element(parent, (Element)current);
+                    }
+                    else if ("elements".equals(localName)) {
+                        elements(parent, (Element)current);
+                    }
+                    else if ("text".equals(localName)) {
+                        text(parent, (Element)current);
+                    }
+                    else if ("comment".equals(localName)
+                         ||  "statement".equals(localName)) {
+                        // ignore comments and statements in output
+                    }
+                    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 = owner.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() {
+        compiledStatements = new HashMap<String, CompiledStatement>();
+    }
+
+    public Builder(Document template) {
+        this();
+        this.template = template;
+        compileStatements();
+    }
+
+    protected void compileStatements() {
+
+        NodeList nodes = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "statement");
+
+        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+            Element stmntElement = (Element)nodes.item(i);
+            String stmnt = trimStatement(stmntElement.getTextContent());
+            if (stmnt == null || stmnt.length() == 0) {
+                throw new IllegalArgumentException("found empty statement");
+            }
+            CompiledStatement cs = new CompiledStatement(stmnt);
+            // For faster lookup store a shortend string into the template.
+            stmnt = "s" + i;
+            stmntElement.setTextContent(stmnt);
+            compiledStatements.put(stmnt, cs);
+        }
+    }
+
+    protected List<Node> rootsToList() {
+
+        NodeList roots = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "template");
+
+        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);
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    protected static final String trimStatement(String stmnt) {
+        if (stmnt == null) return null;
+        //XXX: Maybe a bit to radical for multiline strings?
+        return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" ");
+    }
+
+    protected static Document getOwnerDocument(Node node) {
+        Document document = node.getOwnerDocument();
+        return document != null ? document : (Document)node;
+    }
+
+    private static final List<NamedConnection> wrap(Connection connection) {
+        List<NamedConnection> list = new ArrayList<NamedConnection>(1);
+        list.add(new NamedConnection(DEFAULT_CONNECTION_NAME, connection));
+        return list;
+    }
+
+    public void build(
+        Connection          connection,
+        Node                output,
+        Map<String, Object> parameters
+    )
+    throws SQLException
+    {
+        build(wrap(connection), output, parameters);
+    }
+
+    public void build(
+        List<NamedConnection> connections,
+        Node                  output,
+        Map<String, Object>   parameters
+    )
+    throws SQLException
+    {
+        BuildHelper helper = new BuildHelper(output, connections, parameters);
+
+        helper.build();
+    }
+}
+// vim: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/datacage/templating/CompiledStatement.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,213 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.ArrayList;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import org.apache.log4j.Logger;
+
+public class CompiledStatement
+{
+    private static Logger log = Logger.getLogger(CompiledStatement.class);
+
+    public static final String DATACAGE_DB_CACHE =
+        "datacage.db";
+
+    public static final Pattern VAR =
+        Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}");
+
+    protected String original;
+    protected String statement;
+
+    protected Map<String, List<Integer>> positions;
+
+    protected int numVars;
+
+    public class Instance {
+
+        protected PreparedStatement preparedStatement;
+
+        public Instance() {
+        }
+
+        protected ResultData executeCached(
+            Cache       cache,
+            Connection  connection,
+            StackFrames frames
+        )
+        throws SQLException
+        {
+            log.debug("executeCached");
+            Object [] values = new Object[numVars];
+
+            StringBuilder sb = new StringBuilder(original);
+
+            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+                String key   = entry.getKey();
+                Object value = frames.get(key);
+                sb.append(';').append(key).append(':').append(value);
+                for (Integer index: entry.getValue()) {
+                    values[index] = value;
+                }
+            }
+
+            // XXX: Maybe too many collisions?
+            // String key = original + Arrays.hashCode(values);
+            String key = sb.toString();
+
+            Element element = cache.get(key);
+
+            if (element != null) {
+                return (ResultData)element.getValue();
+            }
+
+            if (preparedStatement == null) {
+                preparedStatement = connection.prepareStatement(statement);
+            }
+
+            for (int i = 0; i < values.length; ++i) {
+                preparedStatement.setObject(i+1, values[i]);
+            }
+
+            ResultData data;
+
+            if (log.isDebugEnabled()) {
+                log.debug("executing: " + statement);
+            }
+
+            ResultSet result = preparedStatement.executeQuery();
+            try {
+                data = new ResultData(preparedStatement.getMetaData())
+                    .addAll(result);
+            }
+            finally {
+                result.close();
+            }
+
+            element = new Element(key, data);
+            cache.put(element);
+
+            return data;
+        }
+
+        protected ResultData executeUncached(
+            Connection  connection,
+            StackFrames frames
+        ) 
+        throws SQLException
+        {
+            log.debug("executeUncached");
+            if (preparedStatement == null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("preparing statement: " + statement);
+                }
+                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+1, value);
+                }
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug("executing: " + statement);
+            }
+
+            ResultSet result = preparedStatement.executeQuery();
+            try {
+                return new ResultData(preparedStatement.getMetaData())
+                    .addAll(result);
+            }
+            finally {
+                result.close();
+            }
+        }
+
+        public ResultData execute(
+            Connection  connection,
+            StackFrames frames,
+            boolean     cached
+        )
+        throws SQLException
+        {
+            if (!cached) {
+                return executeUncached(connection, frames);
+            }
+
+            Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE);
+
+            return cache != null
+                ? executeCached(cache, connection, frames)
+                : executeUncached(connection, frames);
+        }
+
+        public void close() {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                }
+                catch (SQLException sqle) {
+                }
+                preparedStatement = null;
+            }
+        }
+    } // class Instance
+
+    public CompiledStatement() {
+    }
+
+    public CompiledStatement(String original) {
+        this.original = original;
+        // TreeMap to ensure order
+        positions = new TreeMap<String, List<Integer>>();
+        compile();
+    }
+
+    protected void compile() {
+
+        StringBuffer sb = new StringBuffer();
+
+        Matcher m = VAR.matcher(original);
+
+        int index = 0;
+
+        while (m.find()) {
+            String key = m.group(1).toUpperCase();
+            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);
+
+        numVars = index;
+
+        statement = sb.toString();
+    }
+
+    public String getStatement() {
+        return statement;
+    }
+}
+// vim: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/datacage/templating/FunctionResolver.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,108 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.List;
+import java.util.Collection;
+import java.util.Map;
+import java.util.ArrayList;
+
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+
+import javax.xml.namespace.QName;
+
+import org.apache.log4j.Logger;
+
+public class FunctionResolver
+implements   XPathFunctionResolver
+{
+    private static Logger log = Logger.getLogger(FunctionResolver.class);
+
+    public static final String FUNCTION_NAMESPACE_URI = "dc";
+
+    public static final class Entry {
+
+        String        name;
+        XPathFunction function;
+        int           arity;
+
+        public Entry() {
+        }
+
+        public Entry(String name, XPathFunction function, int arity) {
+            this.name     = name;
+            this.function = function;
+            this.arity    = arity;
+        }
+    } // class Entry
+
+    public static final FunctionResolver FUNCTIONS = new FunctionResolver();
+
+    static {
+        FUNCTIONS.addFunction("contains", 2, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                Object haystack = args.get(0);
+                Object needle   = args.get(1);
+
+                if (needle instanceof String) {
+                    needle = ((String)needle).toUpperCase();
+                }
+
+                try {
+                    if (haystack instanceof Collection) {
+                        return Boolean.valueOf(
+                            ((Collection)haystack).contains(needle));
+                    }
+
+                    if (haystack instanceof Map) {
+                        return Boolean.valueOf(
+                            ((Map)haystack).containsKey(needle));
+                    }
+
+                    if (haystack instanceof Object []) {
+                        for (Object straw: (Object [])haystack) {
+                            if (straw.equals(needle)) {
+                                return Boolean.TRUE;
+                            }
+                        }
+                    }
+
+                    return Boolean.FALSE;
+                }
+                catch (Exception e) {
+                    log.error(e);
+                    throw new XPathFunctionException(e);
+                }
+            }
+        });
+    }
+
+    protected List<Entry> functions;
+
+    public FunctionResolver() {
+        functions = new ArrayList<Entry>();
+    }
+
+    public void addFunction(String name, int arity, XPathFunction function) {
+        functions.add(new Entry(name, function, arity));
+    }
+
+    @Override
+    public XPathFunction resolveFunction(QName functionName, int arity) {
+
+        if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) {
+            return null;
+        }
+
+        String name = functionName.getLocalPart();
+        for (Entry entry: functions) {
+            if (entry.arity == arity && entry.name.equals(name)) {
+                return entry.function;
+            }
+        }
+
+        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-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,77 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.io.Serializable;
+
+import java.sql.ResultSetMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+public class ResultData
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(ResultData.class);
+
+    protected String [] columns;
+
+    protected List<Object []> rows;
+
+    public ResultData() {
+        rows = new ArrayList<Object []>();
+    }
+
+    public ResultData(ResultSetMetaData meta)
+    throws SQLException
+    {
+        this();
+
+        boolean debug = log.isDebugEnabled();
+
+        int N = meta.getColumnCount();
+
+        columns = new String[N];
+
+        if (debug) {
+            log.debug("ResultSet column names:");
+        }
+
+        for (int i = 1; i <= N; ++i) {
+            columns[i-1] = meta.getColumnLabel(i).toUpperCase();
+            if (debug) {
+                log.debug("    " + i + ": " + columns[i-1]);
+            }
+        }
+    }
+
+    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;
+    }
+
+    public boolean isEmpty() {
+        return rows.isEmpty();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,127 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.xml.xpath.XPathVariableResolver;
+
+import javax.xml.namespace.QName;
+
+import org.apache.log4j.Logger;
+
+public class StackFrames
+implements   XPathVariableResolver
+{
+    private static Logger log = Logger.getLogger(StackFrames.class);
+
+    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) {
+        key = key.toUpperCase();
+        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 boolean getStore(String key, Object [] result) {
+
+        key = key.toUpperCase();
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            if (frame.containsKey(key)) {
+                result[0] = frame.get(key);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public Object get(String key, Object def) {
+
+        key = key.toUpperCase();
+
+        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;
+    }
+
+    @Override
+    public Object resolveVariable(QName variableName) {
+        if (log.isDebugEnabled()) {
+            log.debug("resolve var: " + variableName);
+        }
+        return get(variableName.getLocalPart());
+    }
+
+    public String dump() {
+        StringBuilder sb = new StringBuilder("[");
+        Set<String> already = new HashSet<String>();
+
+        boolean first = true;
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            for (Map.Entry<String, Object> entry: frame.entrySet()) {
+                if (already.add(entry.getKey())) {
+                    if (first) { first = false;   }
+                    else       { sb.append(", "); }
+                    sb.append('\'').append(entry.getKey())
+                      .append("'='").append(entry.getValue()).append('\'');
+                }
+            }
+        }
+        return sb.append(']').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/datacage/templating/TypeConverter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,31 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+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/geom/Lines.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,280 @@
+package de.intevation.flys.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Line2D;
+
+import de.intevation.flys.artifacts.math.Linear;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+
+public class Lines
+{
+    private static Logger log = Logger.getLogger(Lines.class);
+
+    public static final double EPSILON = 1e-4;
+
+    public static enum Mode { UNDEF, WET, DRY };
+
+    protected Lines() {
+    }
+
+    public static List<Line2D> fillWater(List<Point2D> points, double waterLevel) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("fillWater");
+            log.debug("----------------------------");
+        }
+
+        List<Line2D> result = new ArrayList();
+
+        int N = points.size();
+
+        if (N == 0) {
+            return result;
+        }
+
+        if (N == 1) {
+            Point2D p = points.get(0);
+            // Only generate point if over water
+            if (waterLevel > p.getY()) {
+                result.add(new Line2D.Double(
+                    p.getX(), waterLevel,
+                    p.getX(), waterLevel));
+            }
+            return result;
+        }
+
+        double minX =  Double.MAX_VALUE;
+        double minY =  Double.MAX_VALUE;
+        double maxX = -Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+
+        // To ensure for sequences of equals x's that
+        // the original index order is preserved.
+        for (Point2D p: points) {
+            double x = p.getX(), y = p.getY();
+            if (x < minX) minX = x;
+            if (x > maxX) maxX = x;
+            if (y < minY) minY = y;
+            if (y > maxY) maxY = y;
+        }
+
+        if (minY > waterLevel) { // profile completely over water level
+            log.debug("complete over water");
+            return result;
+        }
+
+        if (waterLevel > maxY) { // water floods profile
+            log.debug("complete under water");
+            result.add(new Line2D.Double(minX, waterLevel, maxX, waterLevel));
+            return result;
+        }
+
+        Mode mode = Mode.UNDEF;
+
+        double startX = minX;
+
+        for (int i = 1; i < N; ++i) {
+            Point2D p1 = points.get(i-1);
+            Point2D p2 = points.get(i);
+
+            if (p1.getY() < waterLevel && p2.getY() < waterLevel) {
+                // completely under water
+                if (debug) {
+                    log.debug("under water: " + p1 + " " + p2);
+                }
+                if (mode != Mode.WET) {
+                    startX = p1.getX();
+                    mode = Mode.WET;
+                }
+                continue;
+            }
+
+            if (p1.getY() > waterLevel && p2.getY() > waterLevel) {
+                if (debug) {
+                    log.debug("over water: " + p1 + " " + p2);
+                }
+                // completely over water
+                if (mode == Mode.WET) {
+                    log.debug("over/wet");
+                    result.add(new Line2D.Double(
+                        startX, waterLevel,
+                        p1.getX(), waterLevel));
+                }
+                mode = Mode.DRY;
+                continue;
+            }
+
+            if (Math.abs(p1.getX() - p2.getX()) < EPSILON) {
+                // vertical line
+                switch (mode) {
+                    case WET:
+                        log.debug("vertical/wet");
+                        mode = Mode.DRY;
+                        result.add(new Line2D.Double(
+                            startX, waterLevel,
+                            p1.getX(), waterLevel));
+                        break;
+                    case DRY:
+                        log.debug("vertical/dry");
+                        mode = Mode.WET;
+                        startX = p2.getX();
+                        break;
+                    default: // UNDEF
+                        log.debug("vertical/undef");
+                        if (p2.getY() < waterLevel) {
+                            mode = Mode.WET;
+                            startX = p2.getX();
+                        }
+                        else {
+                            mode = Mode.DRY;
+                        }
+                }
+                continue;
+            }
+
+            // check if waterlevel directly hits the vertices;
+
+            boolean p1W = Math.abs(waterLevel - p1.getY()) < EPSILON;
+            boolean p2W = Math.abs(waterLevel - p2.getY()) < EPSILON;
+
+            if (p1W || p2W) {
+                if (debug) {
+                    log.debug("water hits vertex: " + p1 + " " + p2 + " " + mode);
+                }
+                if (p1W && p2W) { // parallel to water -> dry
+                    log.debug("water hits both vertices");
+                    if (mode == Mode.WET) {
+                        result.add(new Line2D.Double(
+                            startX, waterLevel,
+                            p1.getX(), waterLevel));
+                    }
+                    mode = Mode.DRY;
+                }
+                else if (p1W) { // p1 == waterlevel
+                    log.debug("water hits first vertex");
+                    if (p2.getY() > waterLevel) { // --> dry
+                        if (mode == Mode.WET) {
+                            result.add(new Line2D.Double(
+                                startX, waterLevel,
+                                p1.getX(), waterLevel));
+                        }
+                        mode = Mode.DRY;
+                    }
+                    else { // --> wet
+                        if (mode != Mode.WET) {
+                            startX = p1.getX();
+                            mode = Mode.WET;
+                        }
+                    }
+                }
+                else { // p2 == waterlevel
+                    log.debug("water hits second vertex");
+                    if (p1.getY() > waterLevel) { // --> wet
+                        if (mode != Mode.WET) {
+                            startX = p2.getX();
+                            mode = Mode.WET;
+                        }
+                    }
+                    else { // --> dry
+                        if (mode == Mode.WET) {
+                            result.add(new Line2D.Double(
+                                startX, waterLevel,
+                                p2.getX(), waterLevel));
+                        }
+                        mode = Mode.DRY;
+                    }
+                }
+                if (debug) {
+                    log.debug("mode is now: " + mode);
+                }
+                continue;
+            }
+
+            // intersection case
+            double x = Linear.linear(
+                waterLevel,
+                p1.getY(), p2.getY(),
+                p1.getX(), p2.getX());
+
+            if (debug) {
+                log.debug("intersection p1:" + p1);
+                log.debug("intersection p2:" + p2);
+                log.debug("intersection at x: " + x);
+            }
+
+            switch (mode) {
+                case WET:
+                    log.debug("intersect/wet");
+                    mode = Mode.DRY;
+                    result.add(new Line2D.Double(
+                        startX, waterLevel,
+                        x, waterLevel));
+                    break;
+
+                case DRY:
+                    log.debug("intersect/dry");
+                    mode   = Mode.WET;
+                    startX = x;
+                    break;
+
+                default: // UNDEF
+                    log.debug("intersect/undef");
+                    if (p2.getY() > waterLevel) {
+                        log.debug("intersect/undef/over");
+                        mode = Mode.DRY;
+                        result.add(new Line2D.Double(
+                            p1.getX(), waterLevel,
+                            x, waterLevel));
+                    }
+                    else {
+                        mode = Mode.WET;
+                        startX = x;
+                    }
+            } // switch mode
+        } // for all points p[i] and p[i-1]
+
+        if (mode == Mode.WET) {
+            result.add(new Line2D.Double(
+                startX, waterLevel,
+                maxX, waterLevel));
+        }
+
+        return result;
+    }
+
+    public static double [][] createWaterLines(
+        List<Point2D> points,
+        double        waterlevel
+    ) {
+        List<Line2D> lines = fillWater(points, waterlevel);
+
+        TDoubleArrayList lxs = new TDoubleArrayList();
+        TDoubleArrayList lys = new TDoubleArrayList();
+
+        for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) {
+            Line2D  l  = iter.next();
+            Point2D p1 = l.getP1();
+            Point2D p2 = l.getP2();
+            lxs.add(p1.getX());
+            lys.add(p1.getY());
+            lxs.add(p2.getX());
+            lys.add(p2.getY());
+            if (iter.hasNext()) {
+                lxs.add(Double.NaN);
+                lys.add(Double.NaN);
+            }
+        }
+
+        return new double [][] { lxs.toNativeArray(), lys.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/geom/Polygon2D.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,430 @@
+package de.intevation.flys.geom;
+
+import java.io.Serializable;
+
+import java.awt.Shape;
+
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Collections;
+
+import de.intevation.flys.artifacts.math.Linear;
+
+import static de.intevation.flys.geom.VectorUtils.X;
+import static de.intevation.flys.geom.VectorUtils.Y;
+import static de.intevation.flys.geom.VectorUtils.EPSILON;
+
+public class Polygon2D
+implements   Serializable
+{
+    public static final Comparator<Point2D> POINT_X_CMP =
+        new Comparator<Point2D>() {
+            public int compare(Point2D a, Point2D b) {
+                double d = X(a) - X(b);
+                if (d < 0d) return -1;
+                return d > 0d ? + 1 : 0;
+            }
+        };
+
+    public static final Comparator<Point2D []> FIRST_POINT_X =
+        new Comparator<Point2D []>() {
+            public int compare(Point2D [] a, Point2D [] b) {
+                if (a.length == 0) return -1; // should not happen.
+                if (b.length == 0) return +1; // should not happen.
+                double d = X(a[0]) - Y(b[0]);
+                if (d < 0d) return -1;
+                return d > 0d ? + 1 : 0;
+            }
+        };
+
+    protected List<Point2D> points;
+
+    public Polygon2D() {
+        points = new ArrayList<Point2D>();
+    }
+
+    public Polygon2D(List<Point2D> points) {
+        this.points = points;
+    }
+
+    public void add(double x, double y) {
+        points.add(new Point2D.Double(x, y));
+    }
+
+    protected static boolean addCheck(Point2D p, List<Point2D> points) {
+        switch (points.size()) {
+            case 0:
+                points.add(p);
+                return true;
+            case 1:
+                if (VectorUtils.epsilonEquals(points.get(0), p)) {
+                    return false;
+                }
+                points.add(p);
+                return true;
+            default:
+                int L = points.size()-1;
+                Point2D last = points.get(L);
+                if (VectorUtils.epsilonEquals(last, p)) {
+                    return false;
+                }
+                Point2D before = points.get(L-1);
+                if (VectorUtils.collinear(before, last, p)) {
+                    points.set(L, p);
+                }
+                else {
+                    points.add(p);
+                }
+                return true;
+        }
+    }
+
+    public boolean addCheck(Point2D p) {
+        return addCheck(p, points);
+    }
+
+    public void addReversed(List<Point2D> other) {
+        for (int i = other.size()-1; i >= 0; --i) {
+            addCheck(other.get(i));
+        }
+    }
+
+    public double area() {
+        double area = 0d;
+
+		for (int i = 0, N = points.size(); i < N; ++i) {
+			int j = (i + 1) % N;
+            Point2D pi = points.get(i);
+            Point2D pj = points.get(j);
+			area += X(pi)*Y(pj);
+			area -= X(pj)*Y(pi);
+		}
+
+        return 0.5d*area;
+    }
+
+    public Shape toShape() {
+        Path2D.Double path = new Path2D.Double();
+
+        int N = points.size();
+
+        if (N > 0) {
+            Point2D p = points.get(0);
+            path.moveTo(X(p), Y(p));
+            for (int i = 1; i < N; ++i) {
+                p = points.get(i);
+                path.lineTo(X(p), Y(p));
+            }
+            path.closePath();
+        }
+
+        return path;
+    }
+
+    protected static List<Point2D []> splitByNaNs(
+        double [] xs,
+        double [] ys
+    ) {
+        List<Point2D []> parts = new ArrayList<Point2D []>();
+
+        List<Point2D> tmp = new ArrayList<Point2D>(xs.length);
+
+        for (int i = 0; i < xs.length; ++i) {
+            double x = xs[i];
+            double y = ys[i];
+
+            if (Double.isNaN(x) || Double.isNaN(y)) {
+                if (!tmp.isEmpty()) {
+                    Point2D [] part = new Point2D[tmp.size()];
+                    parts.add(tmp.toArray(part));
+                    tmp.clear();
+                }
+            }
+            else {
+                tmp.add(new Point2D.Double(x, y));
+            }
+        }
+
+        if (!tmp.isEmpty()) {
+            Point2D [] part = new Point2D[tmp.size()];
+            parts.add(tmp.toArray(part));
+        }
+
+        return parts;
+    }
+
+    protected static boolean removeNoneIntersecting(
+        List<Point2D []> As,
+        List<Point2D []> Bs
+    ) {
+        int B = Bs.size()-1;
+        OUTER: for (int i = 0; i < As.size();) {
+            Point2D [] a = As.get(i);
+            int lo = 0, hi = B;
+            while (lo <= hi) {
+                int mid = (lo + hi) >> 1;
+                Point2D [] b = Bs.get(mid);
+                     if (X(a[0]) > X(b[b.length-1])) lo = mid+1;
+                else if (X(a[a.length-1]) < X(b[0])) hi = mid-1;
+                else {
+                    // found: keep
+                    ++i;
+                    continue OUTER;
+                }
+            }
+            // not found: remove
+            As.remove(i);
+        }
+
+        return As.isEmpty();
+    }
+
+    protected static void buildPolygons(
+        Point2D []      as,
+        Point2D []      bs,
+        Point2D [][]    rest,
+        List<Polygon2D> positives,
+        List<Polygon2D> negatives
+    ) {
+        List<Point2D> apoints = new ArrayList<Point2D>();
+        List<Point2D> bpoints = new ArrayList<Point2D>();
+
+        double ax = X(as[0]);
+        double bx = X(bs[0]);
+
+        int ai = 1;
+        int bi = 1;
+
+        boolean intersect = false;
+
+        if (ax == bx) {
+            apoints.add(as[0]);
+            bpoints.add(bs[0]);
+        }
+        else if (ax > bx) {
+            apoints.add(as[0]);
+            double bx1;
+            while ((bx1 = X(bs[bi])) < ax) ++bi;
+            if (bx1 == ax) {
+                bpoints.add(bs[bi]);
+            }
+            else { // need to calculate start b point.
+                intersect = true;
+                double by1 = Linear.linear(
+                    ax,
+                    X(bs[bi-1]), bx1,
+                    Y(bs[bi-1]), Y(bs[bi]));
+
+                bpoints.add(new Point2D.Double(ax, by1));
+            }
+        }
+        else { // bx > ax: Symmetric case
+            bpoints.add(bs[0]);
+            double ax1;
+            while ((ax1 = X(as[ai])) < bx) ++ai;
+            if (ax1 == bx) {
+                apoints.add(as[ai]);
+            }
+            else { // need to calculate start b point.
+                intersect = true;
+                double ay1 = Linear.linear(
+                    bx,
+                    X(as[ai-1]), ax1,
+                    Y(as[ai-1]), Y(as[ai]));
+
+                apoints.add(new Point2D.Double(bx, ay1));
+            }
+        }
+
+        // now we have a point in each list, decide if neg/pos.
+        boolean neg = Y(bpoints.get(0)) > Y(apoints.get(0));
+
+        // Continue with inner points
+
+        Line line = new Line();
+
+        while (ai < as.length && bi < bs.length) {
+            double xan = X(as[ai]);
+            double xbn = X(bs[bi]);
+            if (xan == xbn) { 
+                double yan = Y(as[ai]);
+                double ybn = Y(bs[ai]);
+
+                if (neg) {
+                    if (yan > ybn) { // intersection
+                        Point2D ip = VectorUtils.intersection(
+                            apoints.get(apoints.size()-1), as[ai],
+                            bpoints.get(bpoints.size()-1), bs[bi]);
+                        addCheck(ip, apoints);
+                        addCheck(ip, bpoints);
+                        Polygon2D p = new Polygon2D(
+                            new ArrayList<Point2D>(apoints));
+                        p.addReversed(bpoints);
+                        negatives.add(p);
+                        apoints.clear();
+                        bpoints.clear();
+                        apoints.add(ip);
+                        bpoints.add(ip);
+                        neg = !neg;
+                    }
+                    else { // no intersection
+                        addCheck(as[ai], apoints);
+                        addCheck(bs[bi], bpoints);
+                    }
+                }
+                else { // not neg
+                    if (ybn > yan) { // intersection
+                        Point2D ip = VectorUtils.intersection(
+                            apoints.get(apoints.size()-1), as[ai],
+                            bpoints.get(bpoints.size()-1), bs[bi]);
+                        addCheck(ip, apoints);
+                        addCheck(ip, bpoints);
+                        Polygon2D p = new Polygon2D(
+                            new ArrayList<Point2D>(apoints));
+                        p.addReversed(bpoints);
+                        positives.add(p);
+                        apoints.clear();
+                        bpoints.clear();
+                        apoints.add(ip);
+                        bpoints.add(ip);
+                        neg = !neg;
+                    }
+                    else { // no intersection
+                        addCheck(as[ai], apoints);
+                        addCheck(bs[bi], bpoints);
+                    }
+                }
+                ++ai;
+                ++bi;
+            }
+            else if (xan > xbn) {
+                line.set(apoints.get(apoints.size()-1), as[ai]);
+                double dir = neg ? -1d: 1d; // XXX: correct sign?
+                while  (bi < bs.length 
+                    && X(bs[bi]) < xan
+                    && line.eval(bs[bi])*dir > EPSILON) 
+                    ++bi;
+                if (bi == bs.length) {
+                    // b run out of points
+                    // calculate Y(last_a, as[ai]) for X(bs[bi-1])
+                    double ay1 = Linear.linear(
+                        X(bs[bi-1]),
+                        X(apoints.get(apoints.size()-1)), X(as[ai]),
+                        Y(apoints.get(apoints.size()-1)), Y(as[ai]));
+                    addCheck(new Point2D.Double(X(bs[bi-1]), ay1), apoints);
+                    addCheck(bs[bi-1], bpoints);
+                    Polygon2D p = new Polygon2D(
+                        new ArrayList<Point2D>(apoints));
+                    p.addReversed(bpoints);
+                    apoints.clear();
+                    bpoints.clear();
+                    (neg ? negatives : positives).add(p);
+                    break;
+                }
+                else {
+                    // TODO: intersect line and/or X(bs[bi]) >= xan?
+                }
+            }
+            else { // xbn > xan
+                line.set(bpoints.get(bpoints.size()-1), bs[bi]);
+                // TODO: continue symmetric
+            }
+        }
+
+        // TODO: Continue with closing segment
+    }
+
+    public static final class Line {
+
+        private double a;
+        private double b;
+        private double c;
+
+        public Line() {
+        }
+
+        public Line(Point2D p1, Point2D p2) {
+            set(p1, p2);
+        }
+
+        public void set(Point2D p1, Point2D p2) {
+            Point2D p3 = 
+                VectorUtils.normalize(
+                VectorUtils.sub(p1, p2));
+
+            Point2D n = VectorUtils.ortho(p3);
+
+            a = X(n);
+            b = Y(n);
+
+            // a*x + b*y + c = 0
+            // c = -a*x -b*y
+
+            c = -a*X(p1) - b*Y(p1);
+        }
+
+        public double eval(Point2D p) {
+            return a*X(p) + b*Y(p) + c;
+        }
+    }
+
+    public static void createPolygons(
+        double [] xAs, double [] yAs,
+        double [] xBs, double [] yBs,
+        List<Polygon2D> positives,
+        List<Polygon2D> negatives
+    ) {
+        if (xAs.length == 0 || xBs.length == 0) {
+            return;
+        }
+
+        List<Point2D []> splAs = splitByNaNs(xAs, yAs);
+        List<Point2D []> splBs = splitByNaNs(xBs, yBs);
+
+        // They feeded us with NaNs only.
+        if (splAs.isEmpty() || splBs.isEmpty()) {
+            return;
+        }
+
+        // Sort each part by x to ensure that the first
+        // is the smallest.
+        for (Point2D [] splA: splAs) {
+            Arrays.sort(splA, POINT_X_CMP);
+        }
+
+        for (Point2D [] splB: splBs) {
+            Arrays.sort(splB, POINT_X_CMP);
+        }
+
+        // Now sort all parts by there first elements.
+        // Should be good enough to find overlapping regions.
+        Collections.sort(splAs, FIRST_POINT_X);
+        Collections.sort(splBs, FIRST_POINT_X);
+
+        // Check if the two series intersect at all.
+        // If no then there will be no area between them.
+
+        Point2D [] p1 = splAs.get(0);
+        Point2D [] p2 = splBs.get(splBs.size()-1);
+
+        // Sort out the ranges that are not intersecting
+        // the ranges in the other series.
+        // We are going to merge them anyway
+        // so this is not strictly required. 
+        // Keep it to recude cases.
+        if (removeNoneIntersecting(splAs, splBs)
+        ||  removeNoneIntersecting(splBs, splAs)
+        ) {
+            // They do not intersect at all.
+            return;
+        }
+
+        // TODO: Intersect/split the two series parts.
+    }
+}
+// vim: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/geom/VectorUtils.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,149 @@
+package de.intevation.flys.geom;
+
+import java.awt.geom.Point2D;
+
+public final class VectorUtils
+{
+    public static final double EPSILON = 1e-4;
+
+    private VectorUtils() {
+    }
+
+    public static final double X(Point2D p) {
+        return p.getX();
+    }
+
+    public static final double Y(Point2D p) {
+        return p.getY();
+    }
+
+    public static final Point2D sub(Point2D a, Point2D b) {
+        return new Point2D.Double(X(a)-X(b), Y(a)-Y(b));
+    }
+
+    public static final double dot(Point2D a, Point2D b) {
+        return X(a)*X(b) + Y(a)*Y(b);
+    }
+
+    public static final Point2D add(Point2D a, Point2D b) {
+        return new Point2D.Double(X(a)+X(b), Y(a)+Y(b));
+    }
+
+    public static final Point2D negate(Point2D a) {
+        return new Point2D.Double(-X(a), -Y(a));
+    }
+
+    public static final Point2D ortho(Point2D a) {
+        return new Point2D.Double(-Y(a), X(a));
+    }
+
+    public static final Point2D scale(Point2D a, double s) {
+        return new Point2D.Double(s*X(a), s*Y(a));
+    }
+
+    public static final double lengthSq(Point2D a) {
+        double x = X(a);
+        double y = Y(a);
+        return x*x + y*y;
+    }
+
+    public static final double length(Point2D a) {
+        return Math.sqrt(lengthSq(a));
+    }
+
+    public static final Point2D normalize(Point2D a) {
+        double length = length(a);
+        return length != 0d
+            ? scale(a, 1d/length) 
+            : new Point2D.Double(X(a), Y(a));
+    }
+
+    public static final double L1(Point2D a, Point2D b) {
+        return Math.abs(X(a)-X(b)) + Math.abs(Y(a)-Y(b));
+    }
+
+    public static final boolean collinear(Point2D a, Point2D b, Point2D c) {
+        double x1 = X(a);
+        double y1 = Y(a);
+        double x2 = X(b);
+        double y2 = Y(b);
+        double x3 = X(c);
+        double y3 = Y(c);
+
+        return Math.abs((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1)) < EPSILON;
+    }
+
+    public static boolean epsilonEquals(Point2D a, Point2D b) {
+        return Math.abs(X(a)-X(b)) < EPSILON 
+            && Math.abs(Y(a)-Y(b)) < EPSILON;
+    }
+
+    public static final Point2D intersection(
+        Point2D p1, Point2D p2,
+        Point2D p3, Point2D p4
+    ) {
+        double x1 = X(p1);
+        double y1 = Y(p1);
+        double x2 = X(p2);
+        double y2 = Y(p2);
+        double x3 = X(p3);
+        double y3 = Y(p3);
+        double x4 = X(p4);
+        double y4 = Y(p4);
+
+        // Compute a1, b1, c1, where line joining points 1 and 2
+        // is "a1 x + b1 y + c1 = 0".
+        double a1 = y2 - y1;
+        double b1 = x1 - x2;
+        double c1 = x2*y1 - x1*y2;
+
+        // Compute r3 and r4.
+        double r3 = a1*x3 + b1*y3 + c1;
+        double r4 = a1*x4 + b1*y4 + c1;
+
+        if (r3 != 0d && r4 != 0d && r3*r4 >= 0) {
+            return null;
+        }
+
+        // Compute a2, b2, c2
+        double a2 = y4 - y3;
+        double b2 = x3 - x4;
+        double c2 = (x4 * y3) - (x3 * y4);
+
+        // Compute r1 and r2
+        double r1 = a2*x1 + b2*y1 + c2;
+        double r2 = a2*x2 + b2*y2 + c2;
+
+        if (r1 != 0d && r2 != 0d && r1*r2 >= 0) {
+            return null;
+        }
+
+        // Line segments intersect: compute intersection point.
+        double denom = a1*b2 - a2*b1;
+
+        if (denom == 0d) { // collinear
+            return null;
+        }
+
+        double offset = Math.abs(denom)/2d;
+
+        // The denom/2 is to get rounding instead of truncating. It
+        // is added or subtracted to the numerator, depending upon the
+        // sign of the numerator.
+        double num = b1*c2 - b2*c1;
+
+        double x = num < 0d
+            ? (num - offset)/denom
+            : (num + offset)/denom;
+
+        num = a2*c1 - a1*c2;
+
+        double y = num < 0d
+            ? (num - offset)/denom
+            : (num + offset)/denom;
+
+        return new Point2D.Double(x, y);
+    }
+
+}
+// vim: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/BackJumpCorrector.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,219 @@
+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;
+
+import de.intevation.flys.utils.DoubleUtil;
+
+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 = DoubleUtil.isIncreasing(ws);
+
+        if (wsUp) {
+            km = DoubleUtil.swapClone(km);
+            ws = DoubleUtil.swapClone(ws);
+        }
+
+        boolean kmUp = DoubleUtil.isIncreasing(km);
+
+        if (!kmUp) {
+            km = DoubleUtil.sumDiffs(km);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("BackJumpCorrector.doCorrection ------- enter");
+            log.debug("  km increasing: " + DoubleUtil.isIncreasing(km));
+            log.debug("  ws increasing: " + DoubleUtil.isIncreasing(ws));
+            log.debug("BackJumpCorrector.doCorrection ------- leave");
+        }
+
+        boolean hasBackJumps = doCorrectionClean(km, ws, errors);
+
+        if (hasBackJumps && wsUp) {
+            // mirror back
+            DoubleUtil.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();
+            }
+
+            double above = aboveWaterKM(km, ws, i);
+
+            if (Double.isNaN(above)) { // run over start km
+                // fill all previous
+                for (int j = 0; j < i; ++j) {
+                    ws[j] = ws[i];
+                }
+                continue;
+            }
+
+            double distance = Math.abs(km[i] - above);
+
+            double quarterDistance = 0.25*distance;
+
+            double start = above - quarterDistance;
+
+            double startHeight = DoubleUtil.interpolateSorted(km, ws, start);
+
+            if (Double.isNaN(startHeight)) {
+                // run over start km
+                startHeight = ws[0];
+            }
+
+            double between = above + quarterDistance;
+
+            double aboveHeight = ws[i] + 0.25*(startHeight - ws[i]);
+
+            double [] x = { start,  above,  between };
+            double [] y = { startHeight, aboveHeight, ws[i] };
+
+            if (log.isDebugEnabled()) {
+                for (int j = 0; j < x.length; ++j) {
+                    log.debug("   " + x[j] + " -> " + y[j]);
+                }
+            }
+
+            if (interpolator == null) {
+                interpolator = new SplineInterpolator();
+            }
+
+            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]));
+                    }
+                }
+
+                int j = i-1;
+
+                for (; j >= 0 && km[j] >= between; --j) {
+                    ws[j] = ws[i];
+                }
+
+                for (; j >= 0 && ws[j] < startHeight; --j) {
+                    ws[j] = spline.value(km[j]);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                // TODO: I18N
+                errors.addProblem("spline interpolation failed.");
+                log.error("spline interpolation failed", aode);
+            }
+        } // for all km
+
+        return !backjumps.isEmpty();
+    }
+
+
+    protected static double aboveWaterKM(
+        double [] km, 
+        double [] ws, 
+        int       wIndex
+    ) {
+        double w = ws[wIndex];
+
+        while (--wIndex >= 0) {
+            // still under water
+            if (ws[wIndex] < w) continue;
+
+            if (ws[wIndex] > w) {
+                // f(ws[wIndex])   = km[wIndex]
+                // f(ws[wIndex+1]) = km[wIndex+1]
+                return Linear.linear(
+                    w,
+                    ws[wIndex], ws[wIndex+1],
+                    km[wIndex], km[wIndex+1]);
+            }
+            else {
+                return km[wIndex];
+            }
+        }
+
+        return Double.NaN;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,70 @@
+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.Facet;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Facet with the curve of a subtraction of two waterlevel-lines.
+ */
+public class DifferenceCurveFacet extends WaterlevelFacet {
+
+    private static Logger logger = Logger.getLogger(DifferenceCurveFacet.class);
+
+
+    public DifferenceCurveFacet() {;}
+
+    public DifferenceCurveFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+
+    ) {
+        super(index, name, description, type, stateID, hash);
+    }
+
+    /**
+     * Get difference curve data.
+     * @return a WKms at given index.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = (CalculationResult)
+            winfo.compute(context, hash, stateID, type, false);
+
+        WKms [] wkms = (WKms [])res.getData();
+
+        WKms result = wkms[index];
+        logger.debug("Got difference curve data (" + result.getName() 
+            + ") at index: " + index);
+
+        return result;
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        WaterlevelFacet copy = new DifferenceCurveFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.stateID = stateID;
+        copy.hash    = hash;
+        return copy;
+    }
+}
+// vim: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:51 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:51 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:51 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/math/WKmsOperation.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,148 @@
+package de.intevation.flys.artifacts.math;
+
+import de.intevation.flys.artifacts.model.WKms;
+import de.intevation.flys.artifacts.model.WKmsImpl;
+
+import java.util.Arrays;
+
+public abstract class WKmsOperation
+{
+    public static final double EPSILON = 1e-6;
+
+    public static final class KmW 
+    implements                Comparable<KmW>
+    {
+        protected double km;
+        protected double w;
+
+        public KmW(double km, double w) {
+            this.km = km;
+            this.w  = w;
+        }
+
+        public int compareTo(KmW other) {
+            return km < other.km
+                ? -1
+                : km > other.km ? +1 : 0;
+        }
+
+        public boolean kmEquals(KmW other) {
+            return Math.abs(km - other.km) < EPSILON;
+        }
+
+        public double subtract(KmW other) {
+            return w - other.w;
+        }
+    } // class KmW
+
+    public static final WKmsOperation SUBTRACTION = new WKmsOperation() {
+
+        @Override
+        public WKms operate(WKms a, WKms b) {
+            return subtract(a, b);
+        }
+    };
+
+    protected WKmsOperation() {
+    }
+
+    public abstract WKms operate(WKms a, WKms b);
+
+    /**
+     * Subtract two series from each other, interpolate values
+     * missing in one series in the other.
+     */
+    public static WKms subtract(WKms minuend, WKms subtrahend) {
+
+        int M = minuend   .size();
+        int S = subtrahend.size();
+
+        // Don't subtract empty sets
+        if (M < 1 || S < 1) {
+            return new WKmsImpl();
+        }
+
+        KmW [] ms = new KmW[M];
+        KmW [] ss = new KmW[S];
+
+        for (int i = 0; i < M; ++i) {
+            ms[i] = new KmW(minuend.getKm(i), minuend.getW(i));
+        }
+
+        for (int i = 0; i < S; ++i) {
+            ss[i] = new KmW(subtrahend.getKm(i), subtrahend.getW(i));
+        }
+
+        Arrays.sort(ms);
+        Arrays.sort(ss);
+
+        // no overlap -> empty result set
+        if (ms[0].km > ss[S-1].km || ss[0].km > ms[M-1].km) {
+            return new WKmsImpl();
+        }
+
+        WKmsImpl result = new WKmsImpl();
+
+        int mi = 0;
+        int si = 0;
+
+        OUT: while (mi < M && si < S) {
+            KmW m = ms[mi];
+            KmW s = ss[si];
+
+            if (m.km + EPSILON < s.km) {
+                // minuend is before subtrahend
+
+                while (ms[mi].km + EPSILON < s.km) {
+                    if (++mi >= M) {
+                        break OUT;
+                    }
+                }
+
+                if (ms[mi].km + EPSILON > s.km) {
+                    double mw = Linear.linear(
+                        s.km,
+                        ms[mi-1].km, ms[mi].km,
+                        ms[mi-1].w,  ms[mi].w);
+                    result.add(s.km, mw - s.w);
+                    ++si;
+                }
+                else { // s.km == ms[mi].km
+                    result.add(s.km, ms[mi].subtract(s));
+                    ++mi;
+                    ++si;
+                }
+            }
+            else if (m.km > s.km + EPSILON) {
+                // subtrahend is before minuend
+
+                while (m.km > ss[si].km + EPSILON) {
+                    if (++si >= S) {
+                        break OUT;
+                    }
+                }
+
+                if (ss[si].km + EPSILON > m.km) {
+                    double sw = Linear.linear(
+                        m.km,
+                        ss[si-1].km, ss[si].km,
+                        ss[si-1].w,  ss[si].w);
+                    result.add(m.km, m.w - sw);
+                }
+                else { // ss[si].km == m.km
+                    result.add(m.km, m.subtract(ss[si]));
+                    ++mi;
+                    ++si;
+                }
+            }
+            else { // m.km == s.km
+                result.add(s.km, m.subtract(s));
+                ++mi;
+                ++si;
+            }
+        }
+
+        return result;
+    }
+}
+// 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/AnnotationFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,81 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.AnnotationArtifact;
+import de.intevation.flys.jfree.FLYSAnnotation;
+import de.intevation.flys.jfree.StickyAxisAnnotation;
+
+import de.intevation.flys.model.Annotation;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+import de.intevation.artifactdatabase.state.Facet;
+
+/**
+ * Facet to access Annotations (landmarks, POIs) of a river.
+ */
+public class AnnotationFacet
+extends      DefaultFacet
+{
+    /** Logger for this class. */
+    private static final Logger logger = Logger.getLogger(AnnotationFacet.class);
+
+
+    /**
+     * Trivial Constructor.
+     */
+    public AnnotationFacet() {
+    }
+
+
+    /**
+     * Trivial Constructor for a AnnotationFacet.
+     *
+     * @param index       Database-Index to use.
+     * @param name        Name (~type) of Facet.
+     * @param description Description of Facet.
+     */
+    public AnnotationFacet(int index, String name, String description) {
+        super(index, name, description);
+    }
+
+
+    /**
+     * Get List of Annotations for river from Artifact.
+     *
+     * @param artifact (Annotation-)Artifact to query for list of Annotations.
+     * @param context  Ignored.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        AnnotationArtifact annotationArtifact = (AnnotationArtifact) artifact;
+
+        List<Annotation>       as = annotationArtifact.getAnnotations();
+        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+
+        for (Annotation a: as) {
+            xy.add(new StickyAxisAnnotation(
+                a.getPosition().getValue(),
+                (float) a.getRange().getA().doubleValue(),
+                StickyAxisAnnotation.SimpleAxis.X_AXIS));
+        }
+
+        return new FLYSAnnotation(description, xy);
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        AnnotationFacet copy = new AnnotationFacet();
+        copy.set(this);
+        return copy;
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,102 @@
+package de.intevation.flys.artifacts.model;
+
+import java.math.BigDecimal;
+
+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());
+    }
+
+
+    /**
+     * Get Annotations which do not have a "b" ("to")-value set.
+     *
+     * @param river name of the river of interest.
+     *
+     * @return List of Annotations for river which have only "a" ("from")
+     *          value set.
+     */
+    public static List<Annotation> getPointAnnotations(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query rangesQuery = session.createQuery(
+            "from Range where river.name=:name and b = null");
+        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 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();
+    }
+
+
+    /**
+     * Get minimal "a" ("from") and maximal "b" ("to") value of annotations'
+     * ranges of a river.
+     *
+     * @param river name of the river of interest.
+     *
+     * @return Array containing minimal "a" and max "b" value of any
+     *         annotation stored for the given river.
+     */
+    public static double[] getAnnotationsBreadth(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query minAQuery = session.createQuery(
+            "select min(a), max(b) from Range where river.name=:name");
+        minAQuery.setParameter("name", river);
+
+        double[] minAmaxB = {0.0f, 0.0f};
+        Object[] row = (Object[]) minAQuery.list().iterator().next();
+        minAmaxB[0] = ((BigDecimal) row[0]).doubleValue();
+        minAmaxB[1] = ((BigDecimal) row[1]).doubleValue();
+        return minAmaxB;
+    }
+
+
+    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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,49 @@
+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);
+
+        if (days == null || days.length == 0) {
+            addProblem(km, "cannot find Ds");
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Calculate duration curve data:");
+            logger.debug("    km       : " + km);
+            logger.debug("    num Days : " + (days != null ? days.length : 0));
+            logger.debug("    num Qs   : " + (qs != null ? qs.length : 0));
+            logger.debug("    result Ws: " + (ws != null ? ws.length : 0));
+        }
+
+        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:51 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/CalculationMessage.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,45 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.artifacts.Message;
+
+
+public class CalculationMessage implements Message {
+
+    protected String message;
+    protected int    steps;
+    protected int    currentStep;
+
+
+    public CalculationMessage() {
+    }
+
+
+    public CalculationMessage(int steps, int currentStep, String message) {
+        this.steps       = steps;
+        this.currentStep = currentStep;
+        this.message     = message;
+    }
+
+
+    public int getSteps() {
+        return steps;
+    }
+
+
+    public int getCurrentStep() {
+        return currentStep;
+    }
+
+
+    public String getMessage() {
+        return message;
+    }
+
+
+    @Override
+    public String getText() {
+        return
+            String.valueOf(currentStep) + "/" + String.valueOf(steps) +
+            " - " + getMessage();
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,41 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Wraps result(s) of a Calculation and eventual error reports.
+ */
+public class CalculationResult
+implements   Serializable
+{
+    protected Object      data;
+    protected Calculation report;
+
+    public CalculationResult() {
+    }
+
+    /**
+     * @param report report (e.g. error messages).
+     */
+    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/CrossSectionFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,55 @@
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * Trival Facet for Cross Sections.
+ */
+public class CrossSectionFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    private static Logger logger = Logger.getLogger(CrossSectionFacet.class);
+
+    protected ComputeType type;
+
+    /** Trivial constructor, set (maybe localized) description. */
+    public CrossSectionFacet(int idx, String description) {
+        super(idx, CROSS_SECTION, description);
+        type = ComputeType.ADVANCE;
+    }
+
+
+    /**
+     * Gets dummy data.
+     */
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for cross section");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        return winfo.getCrossSectionData();
+    }
+
+
+    /** Do a deep copy. */
+    @Override 
+    public Facet deepCopy() {
+        CrossSectionFacet copy = new CrossSectionFacet(this.index, this.description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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/CrossSectionFactory.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,45 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.CrossSection;
+import de.intevation.flys.model.River;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+/**
+ * Get Cross Sections.
+ */
+public class CrossSectionFactory {
+
+    /**
+     * Get CrossSections for an instantiated River.
+     *
+     * @param river river object.
+     *
+     * @return List of Cross Sections of river.
+     */
+    public static List<CrossSection> getCrossSections(River river) {
+        return getCrossSections(river.getName());
+    }
+
+
+    /**
+     * Get Cross Sections for a river by name.
+     *
+     * @param river name of the river of interest.
+     *
+     * @return List of Cross Sections of river.
+     */
+    public static List<CrossSection> getCrossSections(String riverName) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+                "from CrossSection where river.name = :rivername");
+        query.setParameter("rivername", riverName);
+        return query.list();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,51 @@
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+
+/**
+ * Facet for Waterlines in Cross Sections.
+ */
+public class CrossSectionWaterLineFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    private static Logger logger = Logger.getLogger(CrossSectionWaterLineFacet.class);
+
+
+    /** Trivial constructor, set (maybe localized) description. */
+    public CrossSectionWaterLineFacet(int idx, String description) {
+        super(idx, CROSS_SECTION_WATER_LINE, description);
+    }
+
+
+    /**
+     * Gets dummy data.
+     */
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for cross section water line");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        return winfo.getWaterLines(this.getIndex());
+    }
+
+
+    /** Do a deep copy. */
+    @Override 
+    public Facet deepCopy() {
+        CrossSectionWaterLineFacet copy = new CrossSectionWaterLineFacet(this.getIndex(), this.description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,91 @@
+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.artifactdatabase.state.Facet;
+
+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;
+    protected String      stateId;
+
+
+    /** Trivial constructor. */
+    public DataFacet() {
+    }
+
+    /**
+     * Defaults to ADVANCE Compute type.
+     * @param name Name of the facet.
+     * @param description maybe localized description of the facet.
+     */
+    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 DataFacet(
+        String      name,
+        String      description,
+        ComputeType type,
+        String      hash,
+        String      stateId
+    ) {
+        super(name, description);
+        this.type    = type;
+        this.hash    = hash;
+        this.stateId = stateId;
+    }
+
+
+    /**
+     * Return computation result.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+        String    theHash = (hash != null) ? hash : flys.hash();
+
+        return (stateId != null && stateId.length() > 0)
+            ? flys.compute(context, theHash, stateId, type, false)
+            : flys.compute(context, theHash, type, false);
+    }
+
+
+    /**
+     * Return a deep copy.
+     */
+    @Override
+    public Facet deepCopy() {
+        DataFacet copy = new DataFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// 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:51 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:51 2012 +0200
@@ -0,0 +1,46 @@
+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.artifactdatabase.state.Facet;
+
+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() {
+    }
+
+    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();
+    }
+
+    @Override 
+    public Facet deepCopy() {
+        DurationCurveFacet copy = new DurationCurveFacet();
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,47 @@
+package de.intevation.flys.artifacts.model;
+
+public interface FacetTypes {
+
+    String FLOODMAP_WSPLGEN       = "floodmap.wsplgen";
+    String FLOODMAP_BARRIERS      = "floodmap.barriers";
+    String FLOODMAP_RIVERAXIS     = "floodmap.riveraxis";
+    String FLOODMAP_WMSBACKGROUND = "floodmap.wmsbackground";
+    String FLOODMAP_KMS           = "floodmap.kms";
+
+    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 LONGITUDINAL_ANNOTATION = "longitudinal_section.annotations";
+
+    String W_DIFFERENCES = "w_differences";
+
+    String COMPUTED_DISCHARGE_Q = "computed_discharge_curve.q";
+    String COMPUTED_DISCHARGE_MAINVALUES_Q = "computed_discharge_curve.mainvalues.q";
+    String COMPUTED_DISCHARGE_MAINVALUES_W = "computed_discharge_curve.mainvalues.w";
+
+    String MAINVALUES_Q = "mainvalues.q";
+    String MAINVALUES_W = "mainvalues.w";
+
+    String CROSS_SECTION = "cross_section";
+    String CROSS_SECTION_WATER_LINE = "cross_section_water_line";
+
+    String DISCHARGE_CURVE = "discharge_curve.curve";
+
+    String DURATION_W = "duration_curve.w";
+    String DURATION_Q = "duration_curve.q";
+
+    String STATIC_WKMS = "other.wkms";
+    String STATIC_WQKMS = "other.wqkms";
+
+    String HEIGHTMARKS_POINTS = "heightmarks_points";
+
+    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:51 2012 +0200
@@ -0,0 +1,70 @@
+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 Gauge getGauge(String gaugeName) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from Gauge where name=:name");
+        query.setParameter("name", gaugeName);
+
+        List<Gauge> res = query.list();
+
+        return res.isEmpty() ? null : res.get(0);
+    }
+
+
+    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/LayerInfo.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,121 @@
+package de.intevation.flys.artifacts.model;
+
+
+public class LayerInfo {
+
+    protected String name;
+    protected String type;
+    protected String directory;
+    protected String data;
+    protected String extent;
+    protected String group;
+    protected String groupTitle;
+    protected String title;
+    protected String style;
+    protected String filter;
+
+
+    public LayerInfo() {
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public void setDirectory(String directory) {
+        this.directory = directory;
+    }
+
+
+    public String getDirectory() {
+        return directory;
+    }
+
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+
+    public String getData() {
+        return data;
+    }
+
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+
+    public String getGroup() {
+        return group;
+    }
+
+
+    public void setGroupTitle(String groupTitle) {
+        this.groupTitle = groupTitle;
+    }
+
+
+    public String getGroupTitle() {
+        return groupTitle;
+    }
+
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+
+    public String getTitle() {
+        return title;
+    }
+
+
+    public void setExtent(String extent) {
+        this.extent = extent;
+    }
+
+
+    public String getExtent() {
+        return extent;
+    }
+
+
+    public void setStyle(String style) {
+        this.style = style;
+    }
+
+
+    public String getStyle() {
+        return style;
+    }
+
+
+    public void setFilter(String filter) {
+        this.filter = filter;
+    }
+
+
+    public String getFilter() {
+        return filter;
+    }
+}
+// vim: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/MainValuesFactory.java	Fri Sep 28 12:14:51 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/MainValuesQFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,70 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+import de.intevation.flys.artifacts.MainValuesArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.jfree.FLYSAnnotation;
+import de.intevation.flys.jfree.StickyAxisAnnotation;
+
+/**
+ * Facet to show Main Q Values.
+ */
+public class MainValuesQFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Trivial Constructor. */
+    public MainValuesQFacet(String name, String description) {
+        this.description = description;
+        this.name = name;
+        index = 0;
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact;
+
+        List<NamedDouble>      qs = mvArtifact.getMainValuesQ();
+        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+
+        for (NamedDouble q: qs) {
+            xy.add(new StickyAxisAnnotation(
+                q.getName(),
+                (float) q.getValue(),
+                StickyAxisAnnotation.SimpleAxis.X_AXIS));
+        }
+
+        return new FLYSAnnotation(description, xy);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public MainValuesQFacet deepCopy() {
+        MainValuesQFacet copy = new MainValuesQFacet(this.name, description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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/MainValuesWFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,71 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+import de.intevation.flys.artifacts.MainValuesArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.jfree.FLYSAnnotation;
+import de.intevation.flys.jfree.StickyAxisAnnotation;
+
+/**
+ * Facet to show Main W Values.
+ */
+public class MainValuesWFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Trivial Constructor. */
+    public MainValuesWFacet(String name, String description) {
+        this.description = description;
+        this.name = name;
+        index = 0;
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact;
+
+        List<NamedDouble>      ws = mvArtifact.getMainValuesW();
+        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+
+        for (NamedDouble w: ws) {
+            xy.add(new StickyAxisAnnotation(
+                w.getName(),
+                (float) w.getValue(),
+                StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+
+        }
+
+        return new FLYSAnnotation(description, xy);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public MainValuesWFacet deepCopy() {
+        MainValuesWFacet copy = new MainValuesWFacet(this.name, description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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/ManagedDomFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,179 @@
+package de.intevation.flys.artifacts.model;
+
+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;
+
+
+/** 
+ * Use an Element (DOM) to store the information about a facet.
+ * The intent of this facet type is to represent a facet
+ * stored in an Collection attribute. Different facets can have different
+ * attributes that we need to parse, but the only thing ManagedFacets need
+ * to do, is to adjust the attributes "active" and "position". So, those
+ * values are set directly on the Element, the other attributes aren't
+ * touched.
+ */
+public class ManagedDomFacet extends ManagedFacet {
+
+    protected Element facet;
+
+    private static Logger logger = Logger.getLogger(ManagedDomFacet.class);
+
+
+    public ManagedDomFacet(Element facet) {
+        super(null, -1, null, null, -1, -1, -1);
+
+        this.facet = facet;
+    }
+
+
+    @Override
+    public int getIndex() {
+        if (this.index < 0) {
+            String index = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "index");
+
+            if (index != null && index.length() > 0) {
+                this.index = Integer.parseInt(index);
+            }
+        }
+
+        return this.index;
+    }
+
+
+    @Override
+    public String getName() {
+        if (this.name == null || this.name.length() == 0) {
+            String name = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "facet");
+
+            this.name = name;
+        }
+
+        return this.name;
+    }
+
+
+    @Override
+    public String getDescription() {
+        if (this.description == null || this.description.length() == 0) {
+            String description = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "description");
+
+            this.description = description;
+        }
+
+        return this.description;
+    }
+
+
+    @Override
+    public int getPosition() {
+        if (this.position < 0) {
+            String position = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                "pos");
+
+            if (position != null && position.length() > 0) {
+                this.position = Integer.parseInt(position);
+            }
+        }
+
+        return this.position;
+    }
+
+
+    @Override
+    public void setPosition(int position) {
+        this.position = position;
+
+        // TODO Evaluate whether other set/getAttributes also need
+        // to use the NAMESPACE_PREFIX.
+        facet.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX + ":" + "pos",
+            String.valueOf(position));
+    }
+
+
+    @Override
+    public int getActive() {
+        if (this.active < 0) {
+            String active = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "active");
+
+            if (active != null && active.length() > 0) {
+                this.active = Integer.parseInt(active);
+            }
+        }
+
+        return this.active;
+    }
+
+
+    @Override
+    public void setActive(int active) {
+        this.active = active;
+
+        facet.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "art:active",
+            String.valueOf(active));
+    }
+
+
+    @Override
+    public int getVisible() {
+        if (this.visible < 0) {
+            String visible = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "visible");
+
+            if (visible != null && visible.length() > 0) {
+                this.visible = Integer.parseInt(visible);
+            }
+        }
+
+        return this.visible;
+    }
+
+
+    @Override
+    public void setVisible(int visible) {
+        this.visible = visible;
+
+        facet.setAttributeNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "visible",
+            String.valueOf(getVisible()));
+    }
+
+
+    @Override
+    public String getArtifact() {
+        if (this.uuid == null || this.uuid.length() == 0) {
+            String uuid = facet.getAttributeNS(
+                ArtifactNamespaceContext.NAMESPACE_URI, "artifact");
+
+            this.uuid = uuid;
+        }
+
+        return this.uuid;
+    }
+
+
+    /**
+     * Import into document.
+     * @param doc Document to be imported to.
+     */
+    @Override
+    public Node toXML(Document doc) {
+        return doc.importNode(facet, 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/model/ManagedFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,134 @@
+package de.intevation.flys.artifacts.model;
+
+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.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+import de.intevation.artifactdatabase.state.Facet;
+
+
+/**
+ * Facet with user-supplied theme-control-information (pos in list,
+ * active/disabled etc) attached.
+ */
+public class ManagedFacet extends DefaultFacet {
+
+    /** The uuid of the owner artifact. */
+    protected String uuid;
+
+    /** A property that determines the position of this facet. */
+    protected int position;
+
+    /** A property that determines if this facet is active or not. */
+    protected int active;
+
+    /** The logger that is used in this class. */
+    private static Logger logger = Logger.getLogger(ManagedFacet.class);
+
+    /** A property that determines if this facet is visible or not. */
+    protected int visible;
+
+
+    public ManagedFacet() {
+    }
+
+    public ManagedFacet(
+        String  name,
+        int     index,
+        String  desc,
+        String  uuid,
+        int     pos,
+        int     active,
+        int     visible)
+    {
+        super(index, name, desc);
+
+        this.uuid     = uuid;
+        this.position = pos;
+        this.active   = active;
+        this.visible  = visible;
+    }
+
+
+    /**
+     * Sets position (will be merged to position in ThemeList).
+     */
+    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 void setVisible(int visible) {
+        this.visible = visible;
+    }
+
+
+    public int getVisible() {
+        return visible;
+    }
+
+
+    /**
+     * Get uuid of related artifact.
+     * @return uuid of related artifact.
+     */
+    public String getArtifact() {
+        return uuid;
+    }
+
+
+    public Node toXML(Document doc) {
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element facet = ec.create("theme");
+        ec.addAttr(facet, "artifact", getArtifact(), true);
+        ec.addAttr(facet, "facet", getName(), true);
+        ec.addAttr(facet, "pos", String.valueOf(getPosition()), true);
+        ec.addAttr(facet, "active", String.valueOf(getActive()), true);
+        ec.addAttr(facet, "index", String.valueOf(getIndex()), true);
+        ec.addAttr(facet, "description", getDescription(), true);
+        ec.addAttr(facet, "visible", String.valueOf(getVisible()), true);
+
+        return facet;
+    }
+
+    public void set(ManagedFacet other) {
+        uuid     = other.uuid;
+        position = other.position;
+        active   = other.active;
+    }
+
+    @Override 
+    public Facet deepCopy() {
+        ManagedFacet copy = new ManagedFacet();
+        copy.set((DefaultFacet)this);
+        copy.set((ManagedFacet)this);
+        return copy;
+    }
+}
+// vim: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/ManagedFacetAdapter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,67 @@
+package de.intevation.flys.artifacts.model;
+
+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.DefaultFacet;
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+public class ManagedFacetAdapter extends ManagedFacet {
+
+    protected Facet facet;
+
+    public ManagedFacetAdapter() {
+    }
+
+    public ManagedFacetAdapter(
+        Facet   facet,
+        String  uuid,
+        int     pos,
+        int     active,
+        int     visible
+    ) {
+        super(
+            facet.getName(),
+            facet.getIndex(),
+            facet.getDescription(),
+            uuid,
+            pos,
+            active,
+            visible);
+
+        this.facet = facet;
+    }
+
+
+    @Override
+    public Node toXML(Document doc) {
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element e = (Element) facet.toXML(doc);
+        ec.addAttr(e, "artifact", getArtifact(), true);
+        ec.addAttr(e, "facet", getName(), true);
+        ec.addAttr(e, "pos", String.valueOf(getPosition()), true);
+        ec.addAttr(e, "active", String.valueOf(getActive()), true);
+        ec.addAttr(e, "visible", String.valueOf(getVisible()), true);
+
+        return e;
+    }
+
+    @Override
+    public Facet deepCopy() {
+        ManagedFacetAdapter copy = new ManagedFacetAdapter();
+        copy.set((DefaultFacet)this);
+        copy.set((ManagedFacet)this);
+        copy.facet = facet.deepCopy();
+        return facet;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,76 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class MapserverStyle {
+
+    public static class Clazz {
+        protected String name;
+        protected String outlinecolor;
+        protected String symbol;
+        protected int    size;
+
+        public Clazz(String name) {
+            this.name = name;
+        }
+
+        public void setOutlineColor(String outlinecolor) {
+            this.outlinecolor = outlinecolor;
+        }
+
+        public void setSize(int size) {
+            this.size = size;
+        }
+
+        public void setSymbol(String symbol) {
+            if (symbol != null && symbol.length() > 0) {
+                this.symbol = symbol;
+            }
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("CLASS\n");
+            sb.append("NAME \"" + name + "\"\n");
+            sb.append("STYLE\n");
+            sb.append("SIZE " + String.valueOf(size) + "\n");
+            sb.append("OUTLINECOLOR " + outlinecolor + "\n");
+
+            if (symbol != null) {
+                sb.append("SYMBOL '" + symbol + "'\n");
+            }
+
+            sb.append("END\n");
+            sb.append("END\n");
+
+            return sb.toString();
+        }
+    } // end of Clazz
+
+
+    protected List<Clazz> classes;
+
+
+    public MapserverStyle() {
+        classes = new ArrayList<Clazz>();
+    }
+
+    public void addClazz(Clazz clazz) {
+        if (clazz != null) {
+            classes.add(clazz);
+        }
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        for (Clazz clazz: classes) {
+            sb.append(clazz.toString());
+        }
+
+        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/NamedDouble.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.artifacts.model;
+
+/**
+ * Implementation of a <String,double> pair.
+ */
+public class NamedDouble
+extends      NamedObjectImpl
+{
+    protected double value;
+
+
+    /**
+     * @param name  name for the given value.
+     * @param value value.
+     */
+    public NamedDouble(String name, double value) {
+        super(name);
+        this.value = value;
+    }
+
+
+    /**
+     * Get the value.
+     * @return the value.
+     */
+    public double getValue() {
+        return this.value;
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,19 @@
+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 interface NamedObject
+extends          Serializable
+{
+    void setName(String name);
+
+    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-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObjectImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,28 @@
+package de.intevation.flys.artifacts.model;
+
+public class NamedObjectImpl
+implements   NamedObject
+{
+    /** The name of this object.*/
+    protected String name;
+
+    public NamedObjectImpl() {
+    }
+
+    public NamedObjectImpl(String name) {
+        this.name = name;
+    }
+
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    @Override
+    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:51 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.Deque<Node> stack = new java.util.ArrayDeque<Node>();
+            stack.push(root);
+            while (!stack.isEmpty()) {
+                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:51 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:51 2012 +0200
@@ -0,0 +1,64 @@
+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.artifactdatabase.state.Facet;
+
+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;
+    protected String      hash;
+    protected String      stateId;
+
+    public ReportFacet() {
+        this(ComputeType.ADVANCE);
+    }
+
+    public ReportFacet(ComputeType type) {
+        super(0, REPORT, "report");
+        this.type = type;
+    }
+
+
+    public ReportFacet(ComputeType type, String hash, String stateId) {
+        super(0, REPORT, "report");
+        this.type    = type;
+        this.hash    = hash;
+        this.stateId = stateId;
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("get report data");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult cr = (CalculationResult)winfo.compute(
+            context, hash, stateId, type, false);
+
+        return cr.getReport();
+    }
+
+    @Override
+    public Facet deepCopy() {
+        ReportFacet copy = new ReportFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        return copy;
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,71 @@
+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 session.createQuery("from River order by name").list();
+    }
+
+
+    /**
+     * Returns a River object fetched from database based on its id.
+     *
+     * @param river_id The id of the desired river.
+     *
+     * @return the river.
+     */
+    public static River getRiver(int river_id) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery("from River where id=:river_id");
+        query.setParameter("river_id", river_id);
+
+        List<River> rivers = query.list();
+
+        return (rivers != null && rivers.size() > 0) ? rivers.get(0) : null;
+    }
+
+
+    /**
+     * 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:51 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/StaticWKmsCacheKey.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,33 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Caching-Key object for 'static' wst- data.
+ */
+public final class StaticWKmsCacheKey
+implements         Serializable
+{
+    public static final String CACHE_NAME = "wst-value-table-static";
+
+    private int column;
+    private int wst_id;
+
+    public StaticWKmsCacheKey(int column, int wst_id) {
+        this.wst_id  = wst_id;
+        this.column  = column;
+    }
+
+    public int hashCode() {
+        return (wst_id << 8) | column;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticWKmsCacheKey)) {
+            return false;
+        }
+        StaticWKmsCacheKey o = (StaticWKmsCacheKey) other;
+        return wst_id == o.wst_id && this.column == o.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/artifacts/model/StaticWQKmsCacheKey.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,33 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+/**
+ * Caching-Key object for 'static' wst- data.
+ */
+public final class StaticWQKmsCacheKey
+implements         Serializable
+{
+    public static final String CACHE_NAME = "wst-wq--value-table-static";
+
+    private int column;
+    private int wst_id;
+
+    public StaticWQKmsCacheKey(int column, int wst_id) {
+        this.wst_id  = wst_id;
+        this.column  = column;
+    }
+
+    public int hashCode() {
+        return (wst_id << 8) | column;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof StaticWQKmsCacheKey)) {
+            return false;
+        }
+        StaticWQKmsCacheKey o = (StaticWQKmsCacheKey) other;
+        return wst_id == o.wst_id && this.column == o.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/artifacts/model/WKms.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,18 @@
+package de.intevation.flys.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+public interface WKms
+extends          NamedObject
+{
+    int size();
+
+    double getKm(int index);
+
+    double getW(int index);
+
+    TDoubleArrayList allKms();
+
+    TDoubleArrayList allWs();
+}
+// 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/WKmsFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,57 @@
+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.StaticWKmsArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+/**
+ * Facet to show W|km Values.
+ */
+public class WKmsFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Trivial Constructor. */
+    public WKmsFacet(String description) {
+        this(STATIC_WKMS, description);
+    }
+
+    public WKmsFacet(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.index       = 0;
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        StaticWKmsArtifact staticData =
+            (StaticWKmsArtifact) artifact;
+        return staticData.getWKms(0);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public WKmsFacet deepCopy() {
+        WKmsFacet copy = new WKmsFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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/WKmsFactory.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,135 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.type.StandardBasicTypes;
+
+import de.intevation.flys.artifacts.model.WKms;
+import de.intevation.flys.artifacts.model.WKmsImpl;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+/**
+ * Factory to access ready-made WKms for other (than computed) 'kinds' of 
+ * WST-data.
+ */
+public class WKmsFactory
+{
+    private static Logger log = Logger.getLogger(WKmsFactory.class);
+
+    /** Query to get km and ws for wst_id and column_pos. */
+    public static final String SQL_SELECT_WS =
+        "SELECT km, w FROM wst_w_values " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+    /** Query to get name for wst_id and column_pos. */
+    public static final String SQL_SELECT_NAME =
+        "SELECT name " +
+        "FROM wst_columns "+
+        "WHERE wst_id = :wst_id AND position = :column_pos";
+
+
+    private WKmsFactory() {
+    }
+
+
+    /**
+     * Get WKms for given column and wst_id, caring about the cache.
+     */
+    public static WKms getWKms(int column, int wst_id) {
+        log.debug("WKmsFactory.getWKms");
+        Cache cache = CacheFactory.getCache(StaticWKmsCacheKey.CACHE_NAME);
+
+        StaticWKmsCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticWKmsCacheKey(wst_id, column);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static wst values from cache");
+                return (WKms)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WKms values = getWKmsUncached(column, wst_id);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static wst values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+
+    /** Get name for a WKms. */
+    public static String getWKmsName(int column, int wst_id) {
+        log.debug("WKmsFactory.getWKmsName c/" + column + ", wst_id/" + wst_id);
+
+        String name = null;
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_NAME)
+            .addScalar("name", StandardBasicTypes.STRING);
+        nameQuery.setInteger("wst_id",     wst_id);
+        nameQuery.setInteger("column_pos", column);
+
+        List<String> names = nameQuery.list();
+        if (names.size() >= 1) {
+            name = names.get(0);
+        }
+
+        return name;
+    }
+
+
+    /**
+     * Get WKms from db.
+     * @param column the position columns value
+     * @param wst_id database id of the wst
+     * @return according WKms.
+     */
+    public static WKms getWKmsUncached(int column, int wst_id) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("WKmsFactory.getWKmsUncached c/" + column + ", wst_id/" + wst_id);
+        }
+
+        WKmsImpl wkms = new WKmsImpl(getWKmsName(column, wst_id));
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE);
+        sqlQuery.setInteger("wst_id",     wst_id);
+        sqlQuery.setInteger("column_pos", column);
+
+        List<Object []> results = sqlQuery.list();
+
+        double kms [] = new double[results.size()];
+        double ws  [] = new double[results.size()];
+
+        int lastColumn = Integer.MAX_VALUE;
+
+        for (int i = 0, N = results.size(); i < N; i++) {
+            Object[] row = results.get(i);
+            wkms.add((Double) row[0], (Double) row[1]);
+        }
+
+        return wkms;
+    }
+}
+// vim: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/WKmsImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,88 @@
+package de.intevation.flys.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+public class WKmsImpl
+extends      NamedObjectImpl
+implements   WKms
+{
+    protected TDoubleArrayList kms;
+    protected TDoubleArrayList ws;
+
+    public WKmsImpl() {
+        super("");
+        kms = new TDoubleArrayList();
+        ws  = new TDoubleArrayList();
+    }
+
+
+    /**
+     * Create named, empty WKms.
+     */
+    public WKmsImpl(String name) {
+        super(name);
+        kms = new TDoubleArrayList();
+        ws  = new TDoubleArrayList();
+    }
+
+
+    public WKmsImpl(int capacity) {
+        super("");
+        kms = new TDoubleArrayList(capacity);
+        ws  = new TDoubleArrayList(capacity);
+    }
+
+
+    public WKmsImpl(TDoubleArrayList kms, TDoubleArrayList ws) {
+        this(kms, ws, "");
+    }
+
+
+    public WKmsImpl(
+        TDoubleArrayList kms,
+        TDoubleArrayList ws,
+        String           name
+    ) {
+        super(name);
+        this.kms = kms;
+        this.ws  = ws;
+    }
+
+
+    /**
+     * Add a W (in NN+m) for a km (in km).
+     */
+    public void add(double km, double w) {
+        kms.add(km);
+        ws .add(w);
+    }
+
+
+    @Override
+    public double getW(int index) {
+        return ws.getQuick(index);
+    }
+
+
+    @Override
+    public double getKm(int index) {
+        return kms.getQuick(index);
+    }
+
+
+    @Override
+    public int size() {
+        return kms.size();
+    }
+
+    @Override
+    public TDoubleArrayList allKms() {
+        return kms;
+    }
+
+    @Override
+    public TDoubleArrayList allWs() {
+        return ws;
+    }
+}
+// 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/WMSDBLayerFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,73 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+
+public class WMSDBLayerFacet extends WMSLayerFacet {
+
+    protected String data;
+    protected String filter;
+    protected String geometryType;
+
+
+    public WMSDBLayerFacet() {
+        super();
+    }
+
+
+    public WMSDBLayerFacet(int index, String name, String description) {
+        this(index, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    public WMSDBLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+
+    ) {
+        super(index, name, description, type, stateId, hash);
+    }
+
+
+    public WMSDBLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash,
+        String      url
+    ) {
+        super(index, name, description, type, stateId, hash, url);
+    }
+
+
+    public void setFilter(String filter) {
+        this.filter = filter;
+    }
+
+    public String getFilter() {
+        return filter;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setGeometryType(String geometryType) {
+        this.geometryType = geometryType;
+    }
+
+    public String getGeometryType() {
+        return geometryType;
+    }
+}
+// vim: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/WMSLayerFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,155 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.ArrayList;
+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 com.vividsolutions.jts.geom.Envelope;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+import de.intevation.flys.utils.GeometryUtils;
+
+
+public class WMSLayerFacet
+extends      DefaultFacet
+{
+    protected ComputeType  type;
+    protected List<String> layers;
+    protected String       stateId;
+    protected String       hash;
+    protected String       url;
+    protected Envelope     extent;
+    protected String       srid;
+
+
+    private static final Logger logger = Logger.getLogger(WMSLayerFacet.class);
+
+    public WMSLayerFacet() {
+    }
+
+
+    public WMSLayerFacet(int index, String name, String description) {
+        this(index, name, description, ComputeType.FEED, null, null);
+    }
+
+
+    public WMSLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash
+
+    ) {
+        super(index, name, description);
+        this.layers  = new ArrayList<String>();
+        this.type    = type;
+        this.stateId = stateId;
+        this.hash    = hash;
+    }
+
+
+    public WMSLayerFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateId,
+        String      hash,
+        String      url
+    ) {
+        this(index, name, description, type, stateId, hash);
+        this.url = url;
+    }
+
+
+    public void addLayer(String name) {
+        if (name != null && name.length() > 0) {
+            layers.add(name);
+        }
+    }
+
+
+    public void setExtent(Envelope extent) {
+        if (extent != null) {
+            this.extent = extent;
+        }
+    }
+
+
+    public Envelope getExtent() {
+        return extent;
+    }
+
+
+    public void setSrid(String srid) {
+        if (srid != null) {
+            this.srid = srid;
+        }
+    }
+
+
+    public String getSrid() {
+        return srid;
+    }
+
+
+    public Object getData(Artifact artifact, CallContext context) {
+        return null;
+    }
+
+
+    @Override
+    public Node toXML(Document doc) {
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element facet = ec.create("facet");
+        ec.addAttr(facet, "description", description, true);
+        ec.addAttr(facet, "index", String.valueOf(index), true);
+        ec.addAttr(facet, "name", name, true);
+        ec.addAttr(facet, "url", url, true);
+        ec.addAttr(facet, "layers", layers.get(0), true);
+        ec.addAttr(facet, "srid", srid != null ? srid : "", true);
+        ec.addAttr(facet, "extent", extent != null
+            ? GeometryUtils.jtsBoundsToOLBounds(extent)
+            : "", true);
+
+        return facet;
+    }
+
+    @Override
+    public Facet deepCopy() {
+        WMSLayerFacet copy = new WMSLayerFacet();
+        copy.set(this);
+
+        copy.type    = type;
+        copy.layers  = new ArrayList<String>(layers);
+        copy.stateId = stateId;
+        copy.hash    = hash;
+        copy.url     = url;
+        copy.extent  = extent;
+        copy.srid    = srid;
+
+        return copy;
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,172 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.flys.utils.DataUtil;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+public class WQ
+extends      NamedObjectImpl
+{
+    private static Logger logger = Logger.getLogger(WQ.class);
+
+    // TODO: s/w/ws/g
+    protected TDoubleArrayList w;
+
+    // TODO: s/q/qs/g
+    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) {
+        return DataUtil.guessWaterIncreasing(w, factor);
+    }
+
+    public int [] longestIncreasingWRangeIndices() {
+        return longestIncreasingWRangeIndices(new int[2]);
+    }
+
+    public int [] longestIncreasingWRangeIndices(int [] bounds) {
+
+        int N = size();
+        int start = 0;
+        int stop  = 0;
+
+        double lastW = Double.MAX_VALUE;
+
+        for (int i = 0; i < N; ++i) {
+            double v = w.getQuick(i);
+            if (v <= lastW) {
+                if (stop-start > bounds[1]-bounds[0]) {
+                    bounds[0] = start;
+                    bounds[1] = stop;
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("new range: " +
+                            bounds[0] + " - " + bounds[1] + " (" +
+                            w.getQuick(bounds[0]) + ", " +
+                            w.getQuick(bounds[1]) + ")");
+
+                    }
+                }
+                start = stop = i;
+            }
+            else {
+                stop = i;
+            }
+            lastW = v;
+        }
+
+        if (stop-start > bounds[1]-bounds[0]) {
+            bounds[0] = start;
+            bounds[1] = stop;
+            if (logger.isDebugEnabled()) {
+                logger.debug("new range @end: " +
+                    bounds[0] + " - " + bounds[1] + " (" +
+                    w.getQuick(bounds[0]) + ", " +
+                    w.getQuick(bounds[1]) + ")");
+
+            }
+        }
+
+        return bounds;
+    }
+}
+// vim: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:51 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:51 2012 +0200
@@ -0,0 +1,76 @@
+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 boolean isIncreasing() {
+        int lo = getDay(0);
+        int hi = getDay(size()-1);
+
+        return lo < hi;
+    }
+
+
+    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:51 2012 +0200
@@ -0,0 +1,118 @@
+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
+implements   WKms
+{
+    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;
+    }
+
+    @Override
+    public double getKm(int idx) {
+        return kms.getQuick(idx);
+    }
+
+    @Override
+    public TDoubleArrayList allKms() {
+        return kms;
+    }
+
+    @Override
+    public TDoubleArrayList allWs() {
+        return w;
+    }
+
+    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 = getKm(0);
+        double to   = getKm(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/WQKmsFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,57 @@
+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.StaticWQKmsArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+/**
+ * Facet to show W|Q|km Values.
+ */
+public class WQKmsFacet
+extends      DefaultFacet
+implements   FacetTypes {
+
+    /** Trivial Constructor. */
+    public WQKmsFacet(String description) {
+        this(STATIC_WQKMS, description);
+    }
+
+    public WQKmsFacet(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.index       = 0;
+    }
+
+
+    /**
+     * Returns the data this facet requires.
+     *
+     * @param artifact the owner artifact.
+     * @param context  the CallContext (ignored).
+     *
+     * @return the data.
+     */
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        StaticWQKmsArtifact staticData =
+            (StaticWQKmsArtifact) artifact;
+        return staticData.getWQKms(0);
+    }
+
+
+    /**
+     * Create a deep copy of this Facet.
+     * @return a deep copy.
+     */
+    @Override
+    public WQKmsFacet deepCopy() {
+        WQKmsFacet copy = new WQKmsFacet(description);
+        copy.set(this);
+        return copy;
+    }
+}
+// vim: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/WQKmsFactory.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,119 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.type.StandardBasicTypes;
+
+import de.intevation.flys.artifacts.model.WQKms;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+/**
+ * Factory to access ready-made WQKms for other (than computed) 'kinds' of 
+ * WST-data.
+ */
+public class WQKmsFactory
+{
+    private static Logger log = Logger.getLogger(WQKmsFactory.class);
+
+    /** Query to get km and wqs for wst_id and column_pos. */
+    public static final String SQL_SELECT_WQS =
+        "SELECT position, w, q FROM wst_value_table " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+    /** Query to get name for wst_id and column_pos. */
+    public static final String SQL_SELECT_NAME =
+        "SELECT name " +
+        "FROM wst_columns "+
+        "WHERE wst_id = :wst_id AND position = :column_pos";
+
+
+    /** Hidden constructor, use static methods instead. */
+    private WQKmsFactory() {
+        ;
+    }
+
+
+    /**
+     * Get WKms for given column and wst_id, caring about the cache.
+     */
+    public static WQKms getWQKms(int column, int wst_id) {
+        log.debug("WQKmsFactory.getWQKms");
+        Cache cache = CacheFactory.getCache(StaticWQKmsCacheKey.CACHE_NAME);
+
+        StaticWQKmsCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new StaticWQKmsCacheKey(wst_id, column);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got static wst values from cache");
+                return (WQKms)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WQKms values = getWQKmsUncached(column, wst_id);
+
+        if (values != null && cacheKey != null) {
+            log.debug("Store static wst values in cache.");
+            Element element = new Element(cacheKey, values);
+            cache.put(element);
+        }
+        return values;
+    }
+
+
+    /**
+     * Get WQKms from db.
+     * @param column the position columns value
+     * @param wst_id database id of the wst
+     * @return respective WQKms.
+     */
+    public static WQKms getWQKmsUncached(int column, int wst_id) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("WQKmsFactory.getWQKmsUncached, column "
+                + column + ", wst_id " + wst_id);
+        }
+
+        WQKms wqkms = new WQKms(WKmsFactory.getWKmsName(column, wst_id));
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WQS)
+            .addScalar("position", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE)
+            .addScalar("q",  StandardBasicTypes.DOUBLE);
+        sqlQuery.setInteger("wst_id",     wst_id);
+        sqlQuery.setInteger("column_pos", column);
+
+        List<Object []> results = sqlQuery.list();
+
+        double kms [] = new double[results.size()];
+        double ws  [] = new double[results.size()];
+        double qs  [] = new double[results.size()];
+
+        int lastColumn = Integer.MAX_VALUE;
+
+        for (int i = 0, N = results.size(); i < N; i++) {
+            Object[] row = results.get(i);
+            // add(w, q, km)
+            wqkms.add((Double) row[1], (Double) row[2], (Double) row[0]);
+        }
+
+        return wqkms;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WSPLGENCalculation.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,81 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallMeta;
+
+
+public class WSPLGENCalculation extends Calculation {
+
+    private static final Logger log = Logger.getLogger(WSPLGENCalculation.class);
+
+    protected Map<Integer, String> errors;
+    protected Map<Integer, String> warnings;
+
+
+    public WSPLGENCalculation() {
+        errors   = new HashMap<Integer, String>();
+        warnings = new HashMap<Integer, String>();
+    }
+
+
+    public void addError(Integer key, String msg) {
+        log.debug("New error: (" + key + ") " + msg);
+        errors.put(key, msg);
+    }
+
+
+    public void addWarning(Integer key, String msg) {
+        log.debug("New warning: (" + key + ") " + msg);
+        warnings.put(key, msg);
+    }
+
+
+    public int numErrors() {
+        return errors.size();
+    }
+
+
+    public int numWarnings() {
+        return warnings.size();
+    }
+
+
+    public void toXML(Document document, CallMeta meta) {
+        Element root = document.createElement("problems");
+
+        if (numErrors() > 0) {
+            Set<Map.Entry<Integer, String>> entrySet = errors.entrySet();
+
+            for (Map.Entry<Integer, String> entry: entrySet) {
+                Element problem = document.createElement("problem");
+                problem.setAttribute("error", String.valueOf(entry.getKey()));
+                problem.setTextContent(entry.getValue());
+
+                root.appendChild(problem);
+            }
+        }
+
+        if (numWarnings() > 0) {
+            Set<Map.Entry<Integer, String>> entrySet = warnings.entrySet();
+
+            for (Map.Entry<Integer, String> entry: entrySet) {
+                Element problem = document.createElement("problem");
+                problem.setAttribute("error", String.valueOf(entry.getKey()));
+                problem.setTextContent(entry.getValue());
+
+                root.appendChild(problem);
+            }
+        }
+
+        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/WSPLGENJob.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,473 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.wsplgen.FacetCreator;
+
+
+public class WSPLGENJob {
+
+    public static final String GEL_SPERRE   = "SPERRE";
+    public static final String GEL_NOSPERRE = "NOSPERRE";
+
+
+    protected FLYSArtifact artifact;
+
+    protected CallContext callContext;
+
+    protected WSPLGENCalculation calculation;
+
+    protected FacetCreator facetCreator;
+
+    protected File workingDir;
+
+    protected String dgm;
+    protected String pro;
+    protected String wsp;
+    protected String wspTag;
+    protected String axis;
+    protected String area;
+    protected String gel;
+    protected String outFile;
+
+    protected List<String> lin;
+
+    protected int out;
+
+    protected double start;
+    protected double end;
+    protected double from;
+    protected double to;
+    protected double diff;
+    protected double dist;
+
+
+
+    public WSPLGENJob(
+        FLYSArtifact       flys,
+        File               workingDir,
+        FacetCreator       facetCreator,
+        CallContext        context,
+        WSPLGENCalculation calculation)
+    {
+        this.artifact     = flys;
+        this.workingDir   = workingDir;
+        this.facetCreator = facetCreator;
+        this.callContext  = context;
+        this.calculation  = calculation;
+
+        out   = -1;
+        start = Double.NaN;
+        end   = Double.NaN;
+        from  = Double.NaN;
+        to    = Double.NaN;
+        diff  = Double.NaN;
+        dist  = Double.NaN;
+        lin   = new ArrayList<String>(2);
+    }
+
+
+    public File getWorkingDir() {
+        return workingDir;
+    }
+
+
+    public FLYSArtifact getArtifact() {
+        return artifact;
+    }
+
+
+    public FacetCreator getFacetCreator() {
+        return facetCreator;
+    }
+
+
+    public WSPLGENCalculation getCalculation() {
+        return calculation;
+    }
+
+
+    public CallContext getCallContext() {
+        return callContext;
+    }
+
+
+    public void setWsp(String wsp) {
+        this.wsp = wsp;
+    }
+
+
+    public String getWsp() {
+        return wsp;
+    }
+
+
+    public void setWspTag(String wspTag) {
+        this.wspTag = wspTag;
+    }
+
+
+    public String getWspTag() {
+        return wspTag;
+    }
+
+
+    public void addLin(String lin) {
+        this.lin.add(lin);
+    }
+
+
+    public List<String> getLin() {
+        return lin;
+    }
+
+
+    public void setAxis(String axis) {
+        this.axis = axis;
+    }
+
+
+    public String getAxis() {
+        return axis;
+    }
+
+
+    public void setArea(String area) {
+        this.area = area;
+    }
+
+
+    public String getArea() {
+        return area;
+    }
+
+
+    public void setOut(int out) {
+        this.out = out;
+    }
+
+
+    public int getOut() {
+        return out;
+    }
+
+
+    public void setOutFile(String outFile) {
+        this.outFile = outFile;
+    }
+
+
+    public String getOutFile() {
+        return outFile;
+    }
+
+
+    public void setStart(double start) {
+        this.start = start;
+    }
+
+
+    public double getStart() {
+        return start;
+    }
+
+
+    public void setEnd(double end) {
+        this.end = end;
+    }
+
+
+    public double getEnd() {
+        return end;
+    }
+
+
+    public void setPro(String pro) {
+        this.pro = pro;
+    }
+
+
+    public String getPro() {
+        return pro;
+    }
+
+
+    public void setDgm(String dgm) {
+        this.dgm = dgm;
+    }
+
+
+    public String getDgm() {
+        return dgm;
+    }
+
+
+    public void setFrom(double from) {
+        this.from = from;
+    }
+
+
+    public double getFrom() {
+        return from;
+    }
+
+
+    public void setTo(double to) {
+        this.to = to;
+    }
+
+
+    public double getTo() {
+        return to;
+    }
+
+
+    public void setDiff(double diff) {
+        this.diff = diff;
+    }
+
+
+    public double getDiff() {
+        return diff;
+    }
+
+
+    public void setDist(double dist) {
+        this.dist = dist;
+    }
+
+
+    public double getDist() {
+        return dist;
+    }
+
+
+    public void setGel(String gel) {
+        if (gel == null || gel.length() == 0) {
+            return;
+        }
+
+        if (gel.equals(GEL_SPERRE) || gel.equals(GEL_NOSPERRE)) {
+            this.gel = gel;
+        }
+    }
+
+
+    public String getGel() {
+        return gel;
+    }
+
+
+    public void toFile(File file)
+    throws IOException, IllegalArgumentException
+    {
+        PrintWriter writer = null;
+
+        try {
+            writer =
+                new PrintWriter(
+                    new OutputStreamWriter(
+                        new FileOutputStream(file)));
+
+            write(writer);
+        }
+        finally {
+            if (writer != null) {
+                writer.flush();
+                writer.close();
+            }
+        }
+    }
+
+
+    protected void write(PrintWriter writer)
+    throws IOException, IllegalArgumentException
+    {
+        writeWsp(writer);    // required
+        writeWspTag(writer); // required
+        writeLin(writer);
+        writeAxis(writer);
+        writeArea(writer);
+        writeOut(writer);
+        writeOutFile(writer);
+        writeRange(writer);
+        writeDelta(writer);
+        writeGel(writer);
+        writeDist(writer);
+        writePro(writer);
+        writeDgm(writer);    // required
+    }
+
+
+    protected void writeWsp(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String wsp = getWsp();
+
+        if (wsp != null && wsp.length() > 0) {
+            writer.println("-WSP=\"" + wsp + "\"");
+            return;
+        }
+
+        throw new IllegalArgumentException("Required WSP missing!");
+    }
+
+    protected void writeWspTag(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String wspTag = getWspTag();
+
+        if (wspTag != null && wspTag.length() > 0) {
+            writer.println("-WSPTAG=\"" + wspTag + "\"");
+            return;
+        }
+
+        throw new IllegalArgumentException("Required WSPTAG missing!");
+    }
+
+    protected void writeLin(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        List<String> lins = getLin();
+
+        if (lins != null && !lins.isEmpty()) {
+            for (String lin: lins) {
+                writer.println("-LIN=\"" + lin + "\"");
+            }
+        }
+    }
+
+    protected void writeAxis(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String axis = getAxis();
+
+        if (axis != null && axis.length() > 0) {
+            writer.println("-ACHSE=\"" + axis + "\"");
+        }
+    }
+
+    protected void writeGel(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (area != null && area.length() > 0) {
+            writer.println("-GEL=" + getGel());
+        }
+    }
+
+    protected void writeArea(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String area = getArea();
+
+        if (area != null && area.length() > 0) {
+            writer.println("-GEBIET=\"" + area + "\"");
+        }
+    }
+
+
+    protected void writeOut(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        int out = getOut();
+
+        if (out >= 0) {
+            writer.println("-OUTPUT=" + String.valueOf(out));
+        }
+    }
+
+    protected void writeOutFile(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        String outFile = getOutFile();
+
+        if (outFile != null && outFile.length() > 0) {
+            writer.println("-AUSGABE=\""+ outFile + "\"");
+        }
+    }
+
+    protected void writeRange(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        StringBuilder sb = new StringBuilder("-STRECKE=");
+
+        double start = getStart();
+        double end   = getEnd();
+
+        if (Double.isNaN(start) && Double.isNaN(end)) {
+            return;
+        }
+
+        if (! Double.isNaN(getStart())) {
+            sb.append(getStart());
+        }
+
+        sb.append(",");
+
+        if (! Double.isNaN(getEnd())) {
+            sb.append(getEnd());
+        }
+
+        writer.println(sb.toString());
+    }
+
+    protected void writeDelta(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        StringBuilder sb = new StringBuilder("-DELTA=");
+        if (! Double.isNaN(from)) {
+            sb.append(from);
+        }
+
+        sb.append(",");
+
+        if (! Double.isNaN(to)) {
+            sb.append(to);
+        }
+
+        sb.append(",");
+
+        if (! Double.isNaN(diff)) {
+            sb.append(diff);
+        }
+
+        writer.println(sb.toString());
+    }
+
+    protected void writeDist(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (! Double.isNaN(getDist())) {
+            writer.println("-DIST=" + String.valueOf(getDist()));
+        }
+    }
+
+    protected void writePro(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (pro != null && pro.length() > 0) {
+            writer.println("-PRO=\"" + getPro() + "\"");
+        }
+    }
+
+    protected void writeDgm(PrintWriter writer)
+    throws IllegalArgumentException
+    {
+        if (dgm != null && dgm.length() > 0) {
+            writer.println("-DGM=\"" + getDgm() + "\"");
+            return;
+        }
+
+        throw new IllegalArgumentException("Required DGM missing!");
+    }
+}
+// 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/WSPLGENReportFacet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,56 @@
+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.Facet;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+
+/**
+ * This facet is used to provide WSPLGEN reports <b>only</b>.
+ */
+public class WSPLGENReportFacet extends ReportFacet {
+
+    private static Logger logger = Logger.getLogger(WSPLGENReportFacet.class);
+
+
+    protected CalculationResult result;
+
+
+    public WSPLGENReportFacet() {
+    }
+
+
+    public WSPLGENReportFacet(
+        ComputeType       type,
+        String            hash,
+        String            stateId,
+        CalculationResult result
+    ) {
+        super(type, hash, stateId);
+        this.result = result;
+    }
+
+
+    @Override
+    public Object getData(Artifact artifact, CallContext context) {
+        return result.getReport();
+    }
+
+
+    @Override
+    public Facet deepCopy() {
+        WSPLGENReportFacet copy = new WSPLGENReportFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.hash    = hash;
+        copy.stateId = stateId;
+        copy.result  = result;
+        return copy;
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,81 @@
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+/**
+ * Facet of a Waterlevel (WQKms).
+ */
+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() {
+    }
+
+
+    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;
+    }
+
+
+    /**
+     * Get waterlevel data.
+     * @return a WQKms at given index.
+     */
+    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, hash, stateID, type, false);
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        return wqkms[index];
+    }
+
+
+    /** Copy deeply. */
+    @Override
+    public Facet deepCopy() {
+        WaterlevelFacet copy = new WaterlevelFacet();
+        copy.set(this);
+        copy.type    = type;
+        copy.stateID = stateID;
+        copy.hash    = hash;
+        return copy;
+    }
+}
+// vim: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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,657 @@
+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;
+
+    /**
+     * A Column in the table, typically representing one measurement session.
+     */
+    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
+
+    /**
+     * A (weighted) position used for interpolation.
+     */
+    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
+
+    /**
+     * A row, typically a position where measurements whrere taken.
+     */
+    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);
+    }
+
+
+    /**
+     * @param ws (output parameter), gets returned.
+     * @return output parameter ws.
+     */
+    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]);
+    }
+
+
+    /**
+     * Look up interpolation of a Q at given positions.
+     *
+     * @param q           the non-interpolated Q value.
+     * @param referenceKm the reference km (e.g. gauge position).
+     * @param kms         positions for which to interpolate.
+     * @param ws          (output) resulting interpolated ws.
+     * @param qs          (output) resulting interpolated qs.
+     * @param errors      calculation object to store errors.
+     */
+    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:51 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:51 2012 +0200
@@ -0,0 +1,247 @@
+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);
+    }
+
+    /**
+     * @param kind Kind of wst.
+     */
+    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:51 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/CrossSectionKMService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,196 @@
+package de.intevation.flys.artifacts.services;
+
+import de.intevation.artifacts.CallMeta;
+import de.intevation.artifacts.GlobalContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import de.intevation.flys.model.CrossSection;
+import de.intevation.flys.model.CrossSectionLine;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import net.sf.ehcache.Cache;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class CrossSectionKMService
+extends      FLYSService
+{
+    private static Logger logger =
+        Logger.getLogger(CrossSectionKMService.class);
+
+    public static final String CACHE_NAME = "cross-section-kms";
+
+    public CrossSectionKMService() {
+    }
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("CrossSectionKMService.doProcess");
+
+        NodeList crossSectionNodes =
+            data.getElementsByTagName("cross-section");
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        Document document = XMLUtils.newDocument();
+
+        Element all = document.createElement("cross-sections");
+
+        for (int i = 0, CS = crossSectionNodes.getLength(); i < CS; ++i) {
+            Element crossSectionElement = (Element)crossSectionNodes.item(i);
+
+            String idString = crossSectionElement.getAttribute("id");
+            String kmString = crossSectionElement.getAttribute("km");
+            String neighborsString = crossSectionElement.getAttribute("n");
+
+            if (idString.length() == 0 || kmString.length() == 0) {
+                logger.debug("missing attributes in cross-section element");
+                continue;
+            }
+
+            double  km;
+            Integer crossSectionId;
+            int     N = 2;
+
+            try {
+                km             = Double.parseDouble(kmString);
+                crossSectionId = Integer.valueOf(idString);
+
+                if (neighborsString.length() > 0) {
+                    N = Integer.parseInt(neighborsString);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                logger.debug("converting number failed", nfe);
+                continue;
+            }
+
+            NavigableMap<Double, Integer> map;
+
+            if (cache == null) {
+                map = getUncached(crossSectionId);
+            }
+            else {
+                net.sf.ehcache.Element element = cache.get(crossSectionId);
+                if (element == null) {
+                    map = getUncached(crossSectionId);
+                    if (map != null) {
+                        element = new net.sf.ehcache.Element(
+                            crossSectionId, map);
+                        cache.put(element);
+                    }
+                }
+                else {
+                    map = (NavigableMap<Double, Integer>)element.getValue();
+                }
+            }
+
+            if (map == null) {
+                logger.debug("cannot find cross section " + crossSectionId);
+                continue;
+            }
+
+            Deque<Map.Entry<Double, Integer>> result =
+                nearestNeighbors(map, km, N);
+
+            if (!result.isEmpty()) {
+                Element csE = document.createElement("cross-section");
+                csE.setAttribute("id", idString);
+                for (Map.Entry<Double, Integer> entry: result) {
+                    Element lineE = document.createElement("line");
+                    lineE.setAttribute(
+                        "line-id", String.valueOf(entry.getValue()));
+                    lineE.setAttribute(
+                        "km", String.valueOf(entry.getKey()));
+                    csE.appendChild(lineE);
+                }
+                all.appendChild(csE);
+            }
+        }
+
+        document.appendChild(all);
+
+        return document;
+    }
+
+    public static Deque<Map.Entry<Double, Integer>> nearestNeighbors(
+        NavigableMap<Double, Integer> map,
+        double                        km,
+        int                           N
+    ) {
+        Deque<Map.Entry<Double, Integer>> result =
+            new ArrayDeque<Map.Entry<Double, Integer>>(2*N);
+
+        int i = 0;
+        for (Map.Entry<Double, Integer> entry:
+             map.headMap(km, false).descendingMap().entrySet()) {
+            if (i++ >= N) {
+                break;
+            }
+            result.addFirst(entry);
+        }
+
+        i = 0;
+        for (Map.Entry<Double, Integer> entry:
+             map.tailMap(km, false).entrySet()) {
+            if (i++ >= N) {
+                break;
+            }
+            result.addLast(entry);
+        }
+
+        return result;
+    }
+
+    public static NavigableMap<Double, Integer> getUncached(
+        Integer crossSectionId
+    ) {
+        NavigableMap<Double, Integer> result =
+            new ConcurrentSkipListMap<Double, Integer>();
+
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from CrossSection where id=:id");
+        query.setParameter("id", crossSectionId);
+
+        List<CrossSection> crossSections = query.list();
+        if (crossSections.isEmpty()) {
+            return null;
+        }
+
+        CrossSection crossSection = crossSections.get(0);
+        List<CrossSectionLine> lines = crossSection.getLines();
+
+        for (CrossSectionLine line: lines) {
+            Double  km = line.getKm().doubleValue();
+            Integer id = line.getId();
+            result.put(km, id);
+        }
+
+        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/DistanceInfoService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,213 @@
+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.GlobalContext;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+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 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 FLYSService {
+
+    private static enum DistanceFilter {
+        NONE, LOCATIONS, DISTANCES
+    }
+
+    /** 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()";
+
+    public static final String FILTER_XPATH = "/art:river/art:filter/text()";
+
+
+    /**
+     * The default constructor.
+     */
+    public DistanceInfoService() {
+    }
+
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("DistanceInfoService.process");
+
+        String river = XMLUtils.xpathString(
+            data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE);
+
+        String filter  = XMLUtils.xpathString(
+            data, FILTER_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, filter);
+        }
+
+
+        String key = getCacheKey(river, filter);
+
+        net.sf.ehcache.Element element = cache.get(key);
+
+        if (element != null) {
+            logger.debug("distance info found in cache");
+            return (Document)element.getValue();
+        }
+
+        Document result = getUncached(river, filter);
+
+        element = new net.sf.ehcache.Element(key, result);
+
+        logger.debug("store distance info found into cache");
+
+        cache.put(element);
+
+        return result;
+    }
+
+
+    protected String getCacheKey(String river, String filtertype) {
+        return filtertype != null && filtertype.length() > 0
+            ? river + "_" + filtertype
+            : river;
+    }
+
+
+    protected Document getUncached(String river, String filtertype) {
+
+        Document result = XMLUtils.newDocument();
+
+        Iterator<Annotation> iter =
+            AnnotationsFactory.getAnnotationsIterator(river);
+
+        Element all = result.createElement("distances");
+
+        DistanceFilter filter = getDistanceFilter(filtertype);
+
+        while (iter.hasNext()) {
+            Annotation a = iter.next();
+            Element distance = buildDistanceNode(result, a, filter);
+
+            if (distance != null) {
+                all.appendChild(distance);
+            }
+        }
+
+        result.appendChild(all);
+
+        return result;
+    }
+
+
+    protected static DistanceFilter getDistanceFilter(String type) {
+        if (type.equals("locations")) {
+            logger.debug("Found 'location' filter.");
+            return DistanceFilter.LOCATIONS;
+        }
+        else if (type.equals("distances")) {
+            logger.debug("Found 'distances' filter.");
+            return DistanceFilter.DISTANCES;
+        }
+
+        logger.debug("Do not use any filter at all.");
+
+        return DistanceFilter.NONE;
+    }
+
+
+    /**
+     * Builds an Element for a distance info.
+     *
+     * @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,
+        DistanceFilter filter
+    ) {
+        Position   pos   = anno.getPosition();
+        Range      range = anno.getRange();
+        Attribute  attr  = anno.getAttribute();
+        Edge       edge  = anno.getEdge();
+        BigDecimal a     = range.getA();
+        BigDecimal b     = range.getB();
+
+        if (b == null && filter == DistanceFilter.DISTANCES) {
+            return null;
+        }
+
+        if (b != null && filter == DistanceFilter.LOCATIONS) {
+            return null;
+        }
+
+        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/FLYSService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,58 @@
+package de.intevation.flys.artifacts.services;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+import de.intevation.artifacts.CallMeta;
+import de.intevation.artifacts.GlobalContext;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.flys.backend.SessionHolder;
+
+
+public abstract class FLYSService extends DefaultService {
+
+    private static final Logger logger = Logger.getLogger(FLYSService.class);
+
+
+    public Document process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        init();
+
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            shutdown();
+        }
+    }
+
+
+    protected abstract Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta);
+
+
+    protected void init() {
+        logger.debug("init");
+        SessionHolder.acquire();
+    }
+
+
+    protected void shutdown() {
+        logger.debug("shutdown");
+        Session session = SessionHolder.HOLDER.get();
+        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/services/MainValuesService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,308 @@
+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.GlobalContext;
+
+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.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 FLYSService {
+
+    /** 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() {
+    }
+
+
+    @Override
+    public Document doProcess(
+        Document      data,
+        GlobalContext 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]);
+
+            logger.debug("Found gauge: " + gauge.getName());
+
+            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());
+        }
+
+        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;
+    }
+
+
+    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/MapInfoService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,125 @@
+package de.intevation.flys.artifacts.services;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import de.intevation.artifacts.CallMeta;
+import de.intevation.artifacts.GlobalContext;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.model.RiverAxis;
+import de.intevation.flys.utils.GeometryUtils;
+
+/**
+ * This service provides information about the supported rivers by this
+ * application.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapInfoService extends DefaultService {
+
+    /** XPath that points to the river.*/
+    public static final String XPATH_RIVER = "/mapinfo/river/text()";
+
+    public static final String XPATH_RIVER_PROJECTION =
+        "/artifact-database/floodmap/river[@name='%RIVER%']/srid/@value";
+
+    public static final String XPATH_RIVER_BACKGROUND =
+        "/artifact-database/floodmap/river[@name='%RIVER%']/background-wms";
+
+    public static final String XPATH_RIVER_WMS =
+        "/artifact-database/floodmap/river[@name='%RIVER%']/river-wms/@url";
+
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(MapInfoService.class);
+
+
+    /**
+     * The default constructor.
+     */
+    public MapInfoService() {
+    }
+
+
+    public Document process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("MapInfoService.process");
+
+        Document result   = XMLUtils.newDocument();
+        ElementCreator cr = new ElementCreator(result, null, null);
+
+        Element mapinfo = cr.create("mapinfo");
+        result.appendChild(mapinfo);
+
+        String river = extractRiver(data);
+        if (river == null || river.length() == 0) {
+            logger.warn("Cannot generate information: river is empty!");
+            return result;
+        }
+
+        Element root = cr.create("river");
+        cr.addAttr(root, "name", river);
+        mapinfo.appendChild(root);
+
+        RiverAxis axis = RiverAxis.getRiverAxis(river);
+        if (axis != null) {
+            Envelope env    = axis.getGeom().getEnvelopeInternal();
+            String   bounds = GeometryUtils.jtsBoundsToOLBounds(env);
+
+            logger.debug("River '" + river + "' bounds: " + bounds);
+            Element bbox = cr.create("bbox");
+            cr.addAttr(bbox, "value", bounds);
+            root.appendChild(bbox);
+        }
+
+        String xpathS  = XPATH_RIVER_PROJECTION.replace("%RIVER%", river);
+        String sridStr = Config.getStringXPath(xpathS);
+        if (sridStr != null && sridStr.length() > 0) {
+            Element srid = cr.create("srid");
+            cr.addAttr(srid, "value", sridStr);
+            root.appendChild(srid);
+        }
+
+        String xpathB = XPATH_RIVER_BACKGROUND.replace("%RIVER%", river);
+        Element back  = (Element) Config.getNodeXPath(xpathB);
+        if (back != null) {
+            Element background = cr.create("background-wms");
+            cr.addAttr(background, "url", back.getAttribute("url"));
+            cr.addAttr(background, "layers", back.getAttribute("layers"));
+            root.appendChild(background);
+        }
+
+        String xpathWMS = XPATH_RIVER_WMS.replace("%RIVER%", river);
+        String wmsStr   = Config.getStringXPath(xpathWMS);
+        if (wmsStr != null && wmsStr.length() > 0) {
+            Element wms = cr.create("river-wms");
+            cr.addAttr(wms, "url", wmsStr);
+            root.appendChild(wms);
+        }
+
+        return result;
+    }
+
+
+    protected String extractRiver(Document data) {
+        return XMLUtils.xpathString(
+            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,169 @@
+package de.intevation.flys.artifacts.services;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallMeta;
+import de.intevation.artifacts.GlobalContext;
+import de.intevation.artifacts.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.StringUtils;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+
+import de.intevation.flys.artifacts.datacage.Recommendations;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+public class MetaDataService
+extends      FLYSService
+{
+    private static Logger log = Logger.getLogger(MetaDataService.class);
+
+    public static final String XPATH_ARTIFACT_ID = "/art:meta/art:artifact-id/@value";
+    public static final String XPATH_USER_ID     = "/art:meta/art:user-id/@value";
+    public static final String XPATH_OUTS        = "/art:meta/art:outs/@value";
+    public static final String XPATH_PARAMETERS  = "/art:meta/art:parameters/@value";
+
+    /** The global context key of the artifact database. */
+    public static final String ARTIFACT_DATA_BASE_KEY =
+        "global.artifact.database";
+
+    public MetaDataService() {
+    }
+
+    @Override
+    protected Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("MetaDataService.process");
+
+        String artifactId = XMLUtils.xpathString(
+            data, XPATH_ARTIFACT_ID, ArtifactNamespaceContext.INSTANCE);
+
+        if (artifactId != null
+        && (artifactId = artifactId.trim()).length() == 0) {
+            artifactId = null;
+        }
+
+        String userId = XMLUtils.xpathString(
+            data, XPATH_USER_ID, ArtifactNamespaceContext.INSTANCE);
+
+        if (userId != null
+        && (userId = userId.trim()).length() == 0) {
+            userId = null;
+        }
+
+        String outs = XMLUtils.xpathString(
+            data, XPATH_OUTS, ArtifactNamespaceContext.INSTANCE);
+
+        String parameters = XMLUtils.xpathString(
+            data, XPATH_PARAMETERS, ArtifactNamespaceContext.INSTANCE);
+
+        return doService(
+            artifactId, userId, outs, parameters, globalContext);
+    }
+
+    protected static Map<String, Object> splitParameters(
+        String              parameters,
+        Map<String, Object> data
+    ) {
+        if (parameters != null) {
+            String [] parts = parameters.split("\\s*;\\s*");
+            for (String part: parts) {
+                String [] kv = part.split("\\s*:\\s*");
+                if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
+                    continue;
+                }
+                String [] values = kv[1].split("\\s*,\\s*");
+                data.put(kv[0], values.length == 1 ? values[0] : values);
+            }
+        }
+        return data;
+    }
+
+    protected Document doService(
+        String        artifactId,
+        String        userId,
+        String        outsString,
+        String        parameters,
+        GlobalContext globalContext
+    ) {
+        Document result = XMLUtils.newDocument();
+
+        FLYSArtifact flysArtifact;
+
+        if (log.isDebugEnabled()) {
+            log.debug("artifact  : " + artifactId);
+            log.debug("user      : " + userId);
+            log.debug("outs      : " + outsString);
+            log.debug("parameters: " + parameters);
+        }
+
+        if (userId != null && !StringUtils.checkUUID(userId)) {
+            log.warn("'" + userId + "' is not a UUID");
+            return result;
+        }
+
+        if (artifactId != null) {
+            if (!StringUtils.checkUUID(artifactId)) {
+                log.warn("'" + artifactId + "' is not a UUID");
+                return result;
+            }
+
+            Object dbObject =
+                (ArtifactDatabase)globalContext.get(ARTIFACT_DATA_BASE_KEY);
+
+            if (!(dbObject instanceof ArtifactDatabase)) {
+                log.error("Cannot find artifact database");
+                return result;
+            }
+
+            ArtifactDatabase db = (ArtifactDatabase)dbObject;
+
+            Artifact artifact;
+
+            try {
+                artifact = db.getRawArtifact(artifactId);
+            }
+            catch (ArtifactDatabaseException adbe) {
+                log.warn("fetching artifact failed", adbe);
+                return result;
+            }
+
+            if (!(artifact instanceof FLYSArtifact)) {
+                log.warn("artifact is not a FLYS artifact.");
+                return result;
+            }
+
+            flysArtifact = (FLYSArtifact)artifact;
+        }
+        else {
+            flysArtifact = null;
+        }
+
+
+        Map<String, Object> data = splitParameters(
+            parameters, new HashMap<String, Object>());
+
+        String [] outs = outsString == null
+            ? new String [0]
+            : outsString.split("\\s*,\\s*");
+        
+        Recommendations rec = Recommendations.getInstance();
+        rec.recommend(
+            flysArtifact, userId, outs, data, result);
+
+        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/RiverService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,63 @@
+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.GlobalContext;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.model.RiverFactory;
+
+
+/**
+ * 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 FLYSService {
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(RiverService.class);
+
+
+    protected Document doProcess(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        logger.debug("RiverService.process");
+
+        Document result = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            result,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        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);
+
+        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/states/AnnotationRiverState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,63 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.artifacts.model.AnnotationFacet;
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * The only state for an AnnotationArtifact (River is known).
+ */
+public class AnnotationRiverState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** Developer-centric description of facet. */
+    public static final String I18N_DESCRIPTION = "facet.longitudinal_section.annotations";
+
+    /** The logger that is used in this state. */
+    private static final Logger logger = Logger.getLogger(AnnotationRiverState.class);
+
+
+    /**
+     * Add an AnnotationFacet to list of Facets.
+     *
+     * @param artifact Ignored.
+     * @param hash Ignored.
+     * @param context Ignored.
+     * @param meta CallMeta to be used for internationalization.
+     * @param facets List to add AnnotationFacet to.
+     *
+     * @return null.
+     */
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("AnnotationRiverState.computeInit()");
+
+        AnnotationFacet facet = new AnnotationFacet(
+            0,
+            LONGITUDINAL_ANNOTATION,
+            Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION));
+        facets.add(facet);
+
+        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/CalculationSelect.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,126 @@
+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.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";
+
+    /** Constant value for the state w differences calculation. */
+    public static final String CALCULATION_W_DIFFERENCES =
+        "calc.w.differences";
+
+    /** 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,
+        CALCULATION_W_DIFFERENCES };
+
+
+    /** 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;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    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/ComputationRangeState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,269 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.Date;
+import java.util.List;
+
+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.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.WINFOArtifact;
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.GaugesFactory;
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.model.WaterlevelFacet;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputationRangeState
+extends    RangeState
+implements FacetTypes
+{
+    private static Logger logger =
+        Logger.getLogger(ComputationRangeState.class);
+
+
+    /** 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 step width. */
+    public static final int DEFAULT_STEP = 100;
+
+
+
+    public ComputationRangeState() {
+    }
+
+
+    @Override
+    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 = getMinMax(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", String.valueOf(getDefaultStep())});
+            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
+    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 = getSeriesName(context, wqkms[i].getName());
+            facets.add(new WaterlevelFacet(
+                i, DISCHARGE_CURVE, name, ComputeType.FEED, stateID, hash));
+        }
+
+
+        return res;
+    }
+
+    protected String getSeriesName(CallContext cc, String gaugeName) {
+        Gauge gauge = GaugesFactory.getGauge(gaugeName);
+
+        if (gauge == null) {
+            logger.warn("Cannot determine Gauge for name: " + gaugeName);
+            return gaugeName;
+        }
+
+        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 Resources.getMsg(
+                        cc.getMeta(),
+                        "chart.discharge.curve.curve.valid.from",
+                        "",
+                        args);
+                }
+                else {
+                    Object[] args = new Object[] { name, start, end };
+                    return Resources.getMsg(
+                        cc.getMeta(),
+                        "chart.discharge.curve.curve.valid.range",
+                        "",
+                        args);
+                }
+            }
+        }
+
+        return gauge.getName();
+    }
+
+
+    @Override
+    protected double[] getMinMax(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;
+    }
+
+
+    protected double getDefaultStep() {
+        return DEFAULT_STEP;
+    }
+
+
+    @Override
+    protected String getLowerField() {
+        return FROM;
+    }
+
+
+    @Override
+    protected String getUpperField() {
+        return TO;
+    }
+
+
+    @Override
+    protected String getStepField() {
+        return 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/ComputedDischargeCurveState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,87 @@
+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;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+/**
+ * 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[] {
+                    FLYSUtils.getRiver(winfo).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/DGMSelect.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,70 @@
+package de.intevation.flys.artifacts.states;
+
+import java.io.File;
+
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.model.DGM;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DGMSelect extends DefaultState {
+
+    private static final Logger logger = Logger.getLogger(DGMSelect.class);
+
+
+    @Override
+    protected String getUIProvider() {
+        return "dgm_datacage_panel";
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        creator.addAttr(itemElement, "label", getLabel(cc, value), true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    public static String getLabel(CallContext cc, String value) {
+        logger.debug("Create label for value: " + value);
+
+        try {
+            DGM dgm = DGM.getDGM(Integer.parseInt(value));
+
+            File file = new File(dgm.getPath());
+            return file.getName();
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Cannot parse int value: '" + value + "'");
+        }
+
+        return "";
+    }
+}
+// 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:51 2012 +0200
@@ -0,0 +1,396 @@
+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.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+
+import de.intevation.artifactdatabase.data.DefaultStateData;
+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);
+
+    /** The three possible compute types. */
+    public static enum ComputeType {
+        FEED, ADVANCE, INIT
+    }
+
+
+    protected StateData getData(FLYSArtifact artifact,  String name) {
+        return artifact.getData(name);
+    }
+
+
+    /**
+     * Append to a node and return xml description relevant for gui.
+     */
+    public Element describeStatic(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        ElementCreator creator = new 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();
+            appendStaticData(flys, context, creator, ui, name);
+        }
+
+        return ui;
+    }
+
+
+    protected void appendStaticData(
+        FLYSArtifact   flys,
+        CallContext    context,
+        ElementCreator cr,
+        Element        ui,
+        String         name
+    ) {
+        StateData data  = getData(flys, name);
+        String    value = (data != null) ? (String) data.getValue() : null;
+
+        if (value == null) {
+            return;
+        }
+
+        logger.debug("Append element '" + name + "' (" + value + ")");
+
+        Element e = createStaticData(
+            flys, cr, context, name, value, data.getType());
+
+        ui.appendChild(e);
+
+    }
+
+
+    /**
+     * Creates a <i>data</i> element used in the static part of the DESCRIBE
+     * document.
+     *
+     * @param creator The ElementCreator that is used to build new Elements.
+     * @param meta The CallMeta object used for i18n.
+     * @param name The name of the data item.
+     * @param value The value as string.
+     *
+     * @return an Element.
+     */
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        CallMeta meta = cc.getMeta();
+
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, 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 of 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);
+
+        return dataElement;
+    }
+
+
+    public Element describe(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        ElementCreator creator = new 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(
+        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(
+        ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context
+    ) {
+        return null;
+    }
+
+
+    /**
+     * This method is used to create an <i>item</i> Element that contains two
+     * further elements <i>label</i> and <i>value</i>. The label and value
+     * elements both have text nodes.
+     *
+     * @param cr The ElementCreator used to build new Elements.
+     * @param obj This implementation awaits a String array with [0] = label and
+     * [1] = value.
+     *
+     * @return an Element.
+     */
+    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;
+    }
+
+
+    /**
+     * This method transform a given value into a StateData object.
+     *
+     * @param flys The FLYSArtifact.
+     * @param name The name of the data object.
+     * @param val The value of the data object.
+     *
+     * @return a StateData object with <i>name</i> and <i>val</i>ue.
+     */
+    public StateData transform(
+        FLYSArtifact flys,
+        CallContext  cc,
+        String       name,
+        String       val
+    ) {
+        logger.debug("Transform data ('" + name + "','" + val + "')");
+        return new DefaultStateData(name, null, null, val);
+    }
+
+
+    /**
+     * 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.
+     *
+     * @return true, if everything was fine.
+     */
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        return true;
+    }
+
+
+    /**
+     * Returns which UIProvider shall be used to aid user input.
+     */
+    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;
+    }
+
+
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets)
+    {
+        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:51 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/DistanceOnlySelect.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,51 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+
+public class DistanceOnlySelect extends DistanceSelect {
+
+    private static Logger logger = Logger.getLogger(DistanceOnlySelect.class);
+
+    @Override
+    protected String getUIProvider() {
+        return "distance_only_panel";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData dFrom = getData(flys, getLowerField());
+        StateData dTo   = getData(flys, getUpperField());
+
+        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
+        String toStr   = dTo   != null ? (String) dTo.getValue()   : null;
+
+        if (fromStr == null || toStr == null) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        try {
+            double from = Double.parseDouble(fromStr);
+            double to   = Double.parseDouble(toStr);
+
+            double[] minmax = getMinMax(flys);
+
+            return validateBounds(minmax[0], minmax[1], from, to);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_invalid_double_value");
+        }
+    }
+}
+// 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/DistanceSelect.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,24 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceSelect extends ComputationRangeState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(DistanceSelect.class);
+
+
+    public DistanceSelect() {
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "distance_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/DurationCurveState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,98 @@
+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;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+
+/**
+ * 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[] {
+                FLYSUtils.getRiver(winfo).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/FloodMapState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,709 @@
+package de.intevation.flys.artifacts.states;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Polygon;
+
+import org.apache.log4j.Logger;
+
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.FeatureCollections;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.FileTools;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.model.CrossSectionTrack;
+import de.intevation.flys.model.DGM;
+import de.intevation.flys.model.Floodplain;
+import de.intevation.flys.model.RiverAxis;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.CalculationMessage;
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.WSPLGENCalculation;
+import de.intevation.flys.artifacts.model.WSPLGENJob;
+import de.intevation.flys.artifacts.model.WSPLGENReportFacet;
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+import de.intevation.flys.exports.WstWriter;
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.utils.MapfileGenerator;
+import de.intevation.flys.utils.GeometryUtils;
+import de.intevation.flys.wsplgen.FacetCreator;
+import de.intevation.flys.wsplgen.JobObserver;
+import de.intevation.flys.wsplgen.Scheduler;
+
+
+public class FloodMapState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(FloodMapState.class);
+
+
+    public static final String KEEP_ARTIFACT_DIR =
+        System.getProperty("flys.uesk.keep.artifactsdir", "false");
+
+
+    public static final String WSP_ARTIFACT = "wsp";
+
+    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
+
+    public static final String WSPLGEN_PARAMETER_FILE = "wsplgen.par";
+    public static final String WSPLGEN_BARRIERS_LINES = "barrier_lines.shp";
+    public static final String WSPLGEN_BARRIERS_POLY  = "barrier_polygons.shp";
+    public static final String WSPLGEN_AXIS           = "axis.shp";
+    public static final String WSPLGEN_QPS            = "qps.shp";
+    public static final String WSPLGEN_FLOODPLAIN     = "talaue.shp";
+    public static final String WSPLGEN_WSP_FILE       = "waterlevel.wst";
+    public static final String WSPLGEN_OUTPUT_FILE    = "wsplgen.shp";
+
+    public static final int WSPLGEN_DEFAULT_OUTPUT = 0;
+
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("FloodMapState.computeAdvance");
+
+        File artifactDir = getDirectory(artifact);
+
+        if (artifactDir == null) {
+            logger.error("Could not create directory for WSPLGEN results!");
+            return null;
+        }
+
+        WSPLGENCalculation calculation = new WSPLGENCalculation();
+
+        FacetCreator facetCreator = new FacetCreator(
+            artifact, context, hash, getID(), facets);
+
+        WSPLGENJob job = prepareWSPLGENJob(
+            artifact,
+            facetCreator,
+            artifactDir,
+            context,
+            calculation);
+
+        CalculationResult  res   = new CalculationResult(null, calculation);
+        WSPLGENReportFacet report= new WSPLGENReportFacet(
+            ComputeType.ADVANCE, hash, getID(), res);
+
+        facets.add(report);
+
+        if (job == null) {
+            if (KEEP_ARTIFACT_DIR.equals("false")) {
+                removeDirectory(artifact);
+            }
+
+            calculation.addError(-1, Resources.getMsg(
+                context.getMeta(),
+                "wsplgen.job.error",
+                "wsplgen.job.error"));
+
+            logger.error("No WSPLGEN processing has been started!");
+
+            return null;
+        }
+
+        context.afterCall(CallContext.BACKGROUND);
+        context.addBackgroundMessage(new CalculationMessage(
+            JobObserver.STEPS.length,
+            0,
+            Resources.getMsg(
+                context.getMeta(),
+                "wsplgen.job.queued",
+                "wsplgen.job.queued")
+        ));
+
+        Scheduler scheduler = Scheduler.getInstance();
+        scheduler.addJob(job);
+
+        return null;
+    }
+
+
+    /**
+     * Returns (and creates if not existing) the directory for storing WSPLEN
+     * data for the owner artifact.
+     *
+     * @param artifact The owner Artifact.
+     *
+     * @return the directory for WSPLEN data.
+     */
+    protected File getDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_SHAPEFILE_DIR);
+
+        File artifactDir = FileTools.getDirectory(
+            shapePath, artifact.identifier());
+
+        return artifactDir;
+    }
+
+
+    /**
+     * Removes the directory and all its content where the required data and the
+     * results of WSPLGEN are stored. Should be called in endOfLife().
+     */
+    protected void removeDirectory(FLYSArtifact artifact) {
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_SHAPEFILE_DIR);
+
+        File artifactDir = new File(shapePath, artifact.identifier());
+
+        if (artifactDir.exists()) {
+            logger.info("Delete directory: " + artifactDir.getAbsolutePath());
+            boolean success = FileTools.deleteRecursive(artifactDir);
+        }
+        else {
+            logger.debug("There is no directory to remove.");
+        }
+    }
+
+
+    @Override
+    public void endOfLife(Artifact artifact, Object callContext) {
+        logger.info("FloodMapState.endOfLife: " + artifact.identifier());
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        removeDirectory(flys);
+
+        MapfileGenerator.getInstance().update();
+    }
+
+
+    protected WSPLGENJob prepareWSPLGENJob(
+        FLYSArtifact       artifact,
+        FacetCreator       facetCreator,
+        File               artifactDir,
+        CallContext        context,
+        WSPLGENCalculation calculation
+    ) {
+        logger.debug("FloodMapState.prepareWSPLGENJob");
+
+        WSPLGENJob job = new WSPLGENJob(
+            artifact,
+            artifactDir,
+            facetCreator,
+            context,
+            calculation);
+
+        File paraFile = new File(artifactDir, WSPLGEN_PARAMETER_FILE);
+
+        setOut(artifact, job);
+        setRange(artifact, job);
+        setDelta(artifact, job);
+        setGel(artifact, job);
+        setDist(artifact, job);
+        setLine(artifact, facetCreator, artifactDir, job);
+        setAxis(artifact, artifactDir, job);
+        setPro(artifact, artifactDir, job);
+        setDgm(artifact, job);
+        setArea(artifact, artifactDir, job);
+        setOutFile(artifact, job);
+        setWsp(artifact, context, artifactDir, job);    // WSP
+
+        // TODO
+        // setWspTag(artifact, job);
+
+        try {
+            job.toFile(paraFile);
+
+            return job;
+        }
+        catch (IOException ioe) {
+            logger.warn("Cannot write PAR file: " + ioe.getMessage());
+        }
+        catch (IllegalArgumentException iae) {
+            logger.warn("Cannot write PAR file: " + iae.getMessage());
+        }
+
+        return null;
+    }
+
+
+    protected void setOut(FLYSArtifact artifact, WSPLGENJob job) {
+        job.setOut(WSPLGEN_DEFAULT_OUTPUT);
+    }
+
+
+    protected void setRange(FLYSArtifact artifact, WSPLGENJob job) {
+        double[] range = FLYSUtils.getKmRange(artifact);
+
+        job.setStart(range[0]);
+        job.setEnd(range[1]);
+    }
+
+
+    protected void setDelta(FLYSArtifact artifact, WSPLGENJob job) {
+        String from = artifact.getDataAsString("diff_from");
+        String to   = artifact.getDataAsString("diff_to");
+        String diff = artifact.getDataAsString("diff_diff");
+
+        try {
+            job.setFrom(Double.parseDouble(from));
+        }
+        catch (NumberFormatException nfe) {
+        }
+
+        try {
+            job.setTo(Double.parseDouble(to));
+        }
+        catch (NumberFormatException nfe) {
+        }
+
+        try {
+            job.setDiff(Double.parseDouble(diff));
+        }
+        catch (NumberFormatException nfe) {
+        }
+    }
+
+
+    protected void setGel(FLYSArtifact artifact, WSPLGENJob job) {
+        String gel = artifact.getDataAsString("use_floodplain");
+
+        if (gel != null && gel.length() > 0) {
+            boolean use = Boolean.parseBoolean(gel);
+
+            if (use) {
+                job.setGel(WSPLGENJob.GEL_SPERRE);
+            }
+            else {
+                job.setGel(WSPLGENJob.GEL_NOSPERRE);
+            }
+        }
+    }
+
+
+    protected void setDist(FLYSArtifact artifact, WSPLGENJob job) {
+        String dist = artifact.getDataAsString("profile_distance");
+
+        try {
+            job.setDist(Double.parseDouble(dist));
+        }
+        catch (NumberFormatException nfe) {
+            // nothing to do here
+        }
+    }
+
+
+    protected void setLine(
+        FLYSArtifact artifact,
+        FacetCreator facetCreator,
+        File         dir,
+        WSPLGENJob   job
+    ) {
+        String geoJSON = artifact.getDataAsString("uesk.barriers");
+        String srid    = FLYSUtils.getRiverSrid(artifact);
+        String srs     = "EPSG:" + srid;
+
+        if (geoJSON == null || geoJSON.length() == 0) {
+            logger.debug("No barrier features in parameterization existing.");
+            return;
+        }
+
+        SimpleFeatureType ft = getBarriersFeatureType(
+            "barriers", srs, Geometry.class);
+
+        List<SimpleFeature> features = GeometryUtils.parseGeoJSON(geoJSON, ft);
+        if (features == null || features.size() == 0) {
+            logger.debug("No barrier features extracted.");
+            return;
+        }
+
+        FeatureCollection[] fcs = splitLinesAndPolygons(features);
+
+        File shapeLines = new File(dir, WSPLGEN_BARRIERS_LINES);
+        File shapePolys = new File(dir, WSPLGEN_BARRIERS_POLY);
+
+        Object[][] obj = new Object[][] {
+            new Object[] { "typ", String.class }
+        };
+
+        boolean l = GeometryUtils.writeShapefile(
+            shapeLines,
+            GeometryUtils.buildFeatureType("lines", srs, LineString.class, obj),
+            fcs[0]);
+
+        if (l) {
+            logger.debug(
+                "Successfully created barrier line shapefile. " +
+                "Write shapefile path into WSPLGEN job.");
+            job.addLin(shapeLines.getAbsolutePath());
+        }
+
+        boolean p = GeometryUtils.writeShapefile(
+            shapePolys,
+            GeometryUtils.buildFeatureType("polygons", srs, Polygon.class, obj),
+            fcs[1]);
+
+        if (p) {
+            logger.debug(
+                "Successfully created barrier polygon shapefile. " +
+                "Write shapefile path into WSPLGEN job.");
+            job.addLin(shapePolys.getAbsolutePath());
+        }
+
+        if (p || l) {
+            facetCreator.createBarrierFacet();
+        }
+    }
+
+
+    protected SimpleFeatureType getBarriersFeatureType(
+        String name,
+        String srs,
+        Class  type
+    ) {
+        Object[][] attrs = new Object[3][];
+        attrs[0] = new Object[] { "typ", String.class };
+        attrs[1] = new Object[] { "elevation", Double.class };
+        attrs[2] = new Object[] { "mark.selected", Integer.class };
+
+        return GeometryUtils.buildFeatureType(name, srs, type, attrs);
+    }
+
+
+    protected FeatureCollection[] splitLinesAndPolygons(List<SimpleFeature> f) {
+        FeatureCollection lines    = FeatureCollections.newCollection();
+        FeatureCollection polygons = FeatureCollections.newCollection();
+
+        for (SimpleFeature feature: f) {
+            Geometry geom = (Geometry) feature.getDefaultGeometry();
+
+
+            if (geom instanceof LineString) {
+                geom = applyElevationAttribute(feature, (LineString) geom);
+                lines.add(feature);
+            }
+            else if (geom instanceof Polygon) {
+                geom = applyElevationAttribute(feature, (Polygon) geom);
+                polygons.add(feature);
+            }
+            else {
+                logger.warn("Feature not supported: " + geom.getClass());
+            }
+        }
+
+        logger.debug("Found " + lines.size() + " barrier lines.");
+        logger.debug("Found " + polygons.size() + " barrier polygons.");
+
+        return new FeatureCollection[] { lines, polygons };
+    }
+
+
+    protected static Geometry applyElevationAttribute(
+        SimpleFeature feature,
+        Geometry      geom
+    ) {
+        logger.debug("Apply elevations for: " + geom.getClass());
+
+        List<Double> elevations = extractElevations(feature);
+        int           numPoints = geom.getNumPoints();
+        int        numElevation = elevations.size();
+
+        String typ = (String) feature.getAttribute("typ");
+
+        if (numPoints > numElevation) {
+            logger.warn("More vertices in Geometry than elevations given.");
+        }
+
+        Coordinate[] c = geom.getCoordinates();
+        for (int i = 0; i < numPoints; i++) {
+            if (i < numElevation) {
+                c[i].z = elevations.get(i);
+            }
+            else if (typ != null && typ.equals("Graben")) {
+                c[i].z = -9999d;
+            }
+            else {
+                c[i].z = 9999d;
+            }
+        }
+
+        return geom;
+    }
+
+
+    protected static List<Double> extractElevations(SimpleFeature feature) {
+        String tmp = (String) feature.getAttribute("elevation");
+        String typ = (String) feature.getAttribute("typ");
+
+        String[] elevations = tmp == null ? null : tmp.split(" ");
+
+        int num = elevations != null ? elevations.length : 0;
+
+        List<Double> list = new ArrayList<Double>(num);
+
+        for (int i = 0; i < num; i++) {
+            try {
+                list.add(Double.parseDouble(elevations[i]));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Error while parsing elevation at pos: " + i);
+                if (typ != null && typ.equals("Graben")) {
+                    list.add(new Double(-9999.0));
+                }
+                else {
+                    list.add(new Double(9999.0));
+                }
+            }
+        }
+
+        return list;
+    }
+
+
+    protected void setAxis(FLYSArtifact artifact, File dir, WSPLGENJob job) {
+        String river = artifact.getDataAsString("river");
+        String srid    = FLYSUtils.getRiverSrid(artifact);
+        String srs     = "EPSG:" + srid;
+
+        RiverAxis axis = RiverAxis.getRiverAxis(river);
+        if (axis == null) {
+            logger.warn("Could not find river axis for: '" + river + "'");
+            return;
+        }
+
+        Geometry geom = axis.getGeom();
+
+        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
+            "axis", srs, LineString.class);
+
+        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
+        builder.add(geom);
+
+        FeatureCollection collection = FeatureCollections.newCollection();
+        collection.add(builder.buildFeature("0"));
+
+        File axisShape = new File(dir, WSPLGEN_AXIS);
+
+        boolean a = GeometryUtils.writeShapefile(
+            axisShape,
+            GeometryUtils.buildFeatureType("axis", srs, LineString.class),
+            collection);
+
+        if (a) {
+            job.setAxis(axisShape.getAbsolutePath());
+        }
+    }
+
+
+    protected void setPro(FLYSArtifact artifact, File dir, WSPLGENJob job) {
+        String river = artifact.getDataAsString("river");
+        String srid    = FLYSUtils.getRiverSrid(artifact);
+        String srs     = "EPSG:" + srid;
+
+        List<CrossSectionTrack> cst =
+            CrossSectionTrack.getCrossSectionTrack(river);
+
+        logger.debug("Found " + cst.size() + " CrossSectionTracks.");
+
+        Object[][] attrs = new Object[2][];
+        attrs[0] = new Object[] { "ELEVATION", Double.class };
+        attrs[1] = new Object[] { "KILOMETER", Double.class };
+
+        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
+            "qps", srs, LineString.class, attrs);
+
+        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
+        FeatureCollection collection = FeatureCollections.newCollection();
+
+        int i = 0;
+        for (CrossSectionTrack track: cst) {
+            builder.reset();
+            builder.add(track.getGeom());
+            builder.add(track.getZ().doubleValue());
+            builder.add(track.getKm().doubleValue());
+
+            collection.add(builder.buildFeature(String.valueOf(i++)));
+        }
+
+        File qpsShape = new File(dir, WSPLGEN_QPS);
+
+        boolean q = GeometryUtils.writeShapefile(
+            qpsShape,
+            GeometryUtils.buildFeatureType("qps", srs, LineString.class, attrs),
+            collection);
+
+        if (q) {
+            job.setPro(qpsShape.getAbsolutePath());
+        }
+    }
+
+
+    protected void setDgm(FLYSArtifact artifact, WSPLGENJob job) {
+        String dgm_id = artifact.getDataAsString("dgm");
+
+        int id = -1;
+        try {
+            id = Integer.parseInt(dgm_id);
+        }
+        catch (NumberFormatException nfe) { /* do nothing */ }
+
+        DGM dgm = DGM.getDGM(id);
+
+        if (dgm == null) {
+            logger.warn("Could not find specified DGM.");
+
+            return;
+        }
+
+        job.setDgm(dgm.getPath());
+    }
+
+
+    protected void setArea(FLYSArtifact artifact, File dir, WSPLGENJob job) {
+        String river = artifact.getDataAsString("river");
+        String srid  = FLYSUtils.getRiverSrid(artifact);
+        String srs   = "EPSG:" + srid;
+
+        Floodplain plain = Floodplain.getFloodplain(river);
+
+        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
+            "talaue", srs, MultiPolygon.class);
+
+        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
+        builder.add(plain.getGeom());
+
+        FeatureCollection collection = FeatureCollections.newCollection();
+        collection.add(builder.buildFeature("0"));
+
+        File talaueShape = new File(dir, WSPLGEN_FLOODPLAIN);
+
+        boolean t = GeometryUtils.writeShapefile(
+            talaueShape,
+            GeometryUtils.buildFeatureType("talaue", srs, MultiPolygon.class),
+            collection);
+
+        if (t) {
+            job.setArea(talaueShape.getAbsolutePath());
+        }
+    }
+
+
+    protected void setOutFile(FLYSArtifact artifact, WSPLGENJob job) {
+        job.setOutFile(WSPLGEN_OUTPUT_FILE);
+    }
+
+
+    protected WQKms getWQKms(FLYSArtifact flys, CallContext cc) {
+        String   wspString = flys.getDataAsString(WSP_ARTIFACT);
+        String[] parts     = wspString.split(";");
+
+        String otherArtifact = parts[0];
+
+        int idx = -1;
+        try {
+            idx = Integer.parseInt(parts[2]);
+        }
+        catch (NumberFormatException nfe) { /* do nothing */ }
+
+        FLYSArtifact src = otherArtifact != null
+            ? FLYSUtils.getArtifact(otherArtifact, cc)
+            : flys;
+
+        logger.debug("Use waterlevel provided by Artifact: " + src.identifier());
+
+        CalculationResult rawData = (CalculationResult) src.compute(
+            cc,
+            null,
+            WINFO_WSP_STATE_ID,
+            ComputeType.ADVANCE,
+            false);
+
+        WQKms[] wqkms = (WQKms[]) rawData.getData();
+
+        return wqkms == null || idx == -1 || idx >= wqkms.length
+            ? null
+            : wqkms[idx];
+    }
+
+
+    protected void setWsp(
+        FLYSArtifact artifact,
+        CallContext  context,
+        File         dir,
+        WSPLGENJob   job)
+    {
+        logger.debug("FloodMapState.setWsp");
+
+        WQKms data = getWQKms(artifact, context);
+
+        if (data == null) {
+            logger.warn("No WST data found!");
+            return;
+        }
+
+        WstWriter writer = new WstWriter(1);
+
+        // TODO REMOVE job.setWspTag(...) This is only used until the user is
+        // able to select the WSP column himself!
+        boolean writeWspTag = true;
+
+        double[] buf = new double[4];
+        logger.debug("Add WST column: " + data.getName());
+        writer.addColumn(data.getName());
+
+        if (writeWspTag) {
+            job.setWspTag(data.getName());
+            writeWspTag = false;
+        }
+
+        for (int i = 0, num = data.size(); i < num; i++) {
+            data.get(i, buf);
+            writer.add(buf);
+        }
+
+        FileOutputStream fout = null;
+
+        try {
+            File wspFile = new File(dir, WSPLGEN_WSP_FILE);
+            fout         = new FileOutputStream(wspFile);
+
+            writer.write(fout);
+
+            job.setWsp(wspFile.getAbsolutePath());
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.warn("Error while writing wsp file: " + fnfe.getMessage());
+        }
+        finally {
+            if (fout != null) {
+                try {
+                    fout.close();
+                }
+                catch (IOException ioe) { /* do nothing */ }
+            }
+        }
+    }
+}
+// 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/FloodplainChoice.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,63 @@
+package de.intevation.flys.artifacts.states;
+
+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.flys.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FloodplainChoice extends DefaultState {
+
+    public static final String OPTION = "floodplain.option";
+
+
+    @Override
+    protected String getUIProvider() {
+        return "boolean_panel";
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta = context.getMeta();
+
+        Element option = createItem(
+            cr,
+            new String[] { Resources.getMsg(meta, OPTION, OPTION), "true" });
+
+        return new Element[] { option };
+    }
+
+
+    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;
+    }
+}
+// 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:51 2012 +0200
@@ -0,0 +1,146 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationDistanceSelect
+extends      ComputationRangeState
+{
+
+    /** The logger used in this class.*/
+    private static Logger logger = Logger.getLogger(LocationDistanceSelect.class);
+
+
+    /** 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 default constructor that initializes an empty State object.
+     */
+    public LocationDistanceSelect() {
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "location_distance_panel";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("LocationDistanceSelect.validate");
+
+        WINFOArtifact flys = (WINFOArtifact) artifact;
+
+        if (flys.isRange()) {
+            return super.validate(flys);
+        }
+        else {
+            return validateLocations(flys);
+        }
+    }
+
+
+    protected boolean validateLocations(WINFOArtifact flys)
+    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 = getMinMax(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;
+    }
+
+
+    /**
+     * 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(WINFOArtifact 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();
+    }
+}
+// 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:51 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 = getMinMax(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)
+    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 = getMinMax(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/OutputState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,43 @@
+package de.intevation.flys.artifacts.states;
+
+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.CallContext;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+
+public class OutputState extends DefaultState implements FacetTypes {
+
+    private static final Logger logger = Logger.getLogger(OutputState.class);
+
+
+    @Override
+    public Element describeStatic(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        return null;
+    }
+
+
+    @Override
+    public Element describe(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        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-artifacts/src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.artifacts.states;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ProfileDistanceSelect extends DefaultState {
+
+    @Override
+    protected String getUIProvider() {
+        return "auto_integer";
+    }
+}
+// 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:51 2012 +0200
@@ -0,0 +1,106 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+
+/**
+ * @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 Logger logger = Logger.getLogger(RangeState.class);
+
+
+    public RangeState() {
+    }
+
+
+    protected abstract String   getLowerField();
+    protected abstract String   getUpperField();
+    protected abstract String   getStepField();
+    protected abstract double[] getMinMax(Artifact artifact);
+
+
+    protected boolean validateBounds(
+        double fromValid, double toValid,
+        double from,      double to)
+    throws IllegalArgumentException
+    {
+        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");
+        }
+
+        return true;
+    }
+
+
+    /**
+     * 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");
+
+        // XXX The step width is not validated at the moment!
+        return validateBounds(fromValid, toValid, from, to);
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData dFrom = getData(flys, getLowerField());
+        StateData dTo   = getData(flys, getUpperField());
+        StateData dStep = getData(flys, getStepField());
+
+        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[] minmax = getMinMax(flys);
+
+            return validateBounds(minmax[0], minmax[1], from, to, step);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_invalid_double_value");
+        }
+    }
+}
+// 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/RiverAxisState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,79 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.model.WMSDBLayerFacet;
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.utils.GeometryUtils;
+
+
+public class RiverAxisState extends OutputState {
+
+    public static final String I18N_DESCRIPTION = "floodmap.riveraxis";
+
+
+    private static final Logger logger = Logger.getLogger(RiverAxisState.class);
+
+
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("RiverAxisState.computeInit()");
+
+        FLYSContext flysContext = null;
+
+        if (context instanceof FLYSContext) {
+            flysContext = (FLYSContext) context;
+        }
+        else {
+            flysContext = (FLYSContext) ((CallContext) context).globalContext();
+        }
+
+        String river = artifact.getDataAsString("river");
+
+        if(river == null || river.length() == 0) {
+            logger.warn("No river found in the current parameterization.");
+            return null;
+        }
+
+        String url = FLYSUtils.getUserWMSUrl(artifact.identifier());
+
+        WMSDBLayerFacet facet = new WMSDBLayerFacet(
+            0,
+            FLOODMAP_RIVERAXIS,
+            Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION),
+            ComputeType.INIT,
+            getID(), hash,
+            url);
+
+        String name = artifact.identifier() + "-" + FLOODMAP_RIVERAXIS;
+
+        facet.addLayer(name);
+        facet.setExtent(GeometryUtils.getRiverBoundary(river));
+        facet.setSrid(FLYSUtils.getRiverSrid(artifact));
+        facet.setData("geom FROM river_axes USING UNIQUE id USING SRID 31466");
+        facet.setFilter("river_id=1");
+        facet.setGeometryType("LINE");
+
+        facets.add(facet);
+
+        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-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java	Fri Sep 28 12:14:51 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)
+    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/ScenarioSelect.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,120 @@
+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.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+
+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 ScenarioSelect extends DefaultState {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(ScenarioSelect.class);
+
+
+    public static final String FIELD_MODE     = "scenario";
+    public static final String FIELD_BARRIERS = "uesk.barriers";
+
+    public static final String SCENARIO_CURRENT   = "scenario.current";
+    public static final String SCENARIO_POTENTIEL = "scenario.potentiel";
+    public static final String SCENARIO_SCENRAIO  = "scenario.scenario";
+
+    public static final String[] SCENARIOS = {
+        SCENARIO_CURRENT,
+        SCENARIO_POTENTIEL,
+        SCENARIO_SCENRAIO };
+
+
+
+    @Override
+    protected String getUIProvider() {
+        return "map_digitize";
+    }
+
+
+    @Override
+    protected void appendStaticData(
+        FLYSArtifact   flys,
+        CallContext    cc,
+        ElementCreator creator,
+        Element        ui,
+        String         name
+    ) {
+        if (name != null && name.equals(FIELD_BARRIERS)) {
+            return;
+        }
+        else {
+            super.appendStaticData(flys, cc, creator, ui, name);
+        }
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta = context.getMeta();
+
+        if (name.equals(FIELD_MODE)) {
+            Element[] scenarios = new Element[SCENARIOS.length];
+
+            int i = 0;
+
+            for (String scenario: SCENARIOS) {
+                scenarios[i++] = createItem(
+                    cr, new String[] {
+                        Resources.getMsg(meta, scenario, scenario),
+                        scenario
+                    });
+            }
+
+            return scenarios;
+        }
+        else {
+            FLYSArtifact flys = (FLYSArtifact) artifact;
+            String       data = flys.getDataAsString(name);
+
+            return new Element[] { createItem(
+                cr,
+                new String[] {
+                    Resources.getMsg(meta, name, name),
+                    data
+                }
+            )};
+        }
+    }
+
+
+    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;
+    }
+}
+// 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:51 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/StaticState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,32 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+/**
+ * Yet, a non-abstract DefaultState.
+ */
+public class StaticState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static final Logger logger = Logger.getLogger(StaticState.class);
+
+
+    /**
+     * Trivial constructor, sets id and description.
+     * @param id String used for both id and description.
+     */
+    public StaticState(String id) {
+        this(id, id);
+    }
+
+
+    public StaticState(String id, String description) {
+        super();
+        setID(id);
+        setDescription(description);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,178 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.Artifact;
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.StaticWKmsArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.math.WKmsOperation;
+
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.DataFacet;
+import de.intevation.flys.artifacts.model.DifferenceCurveFacet;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WKms;
+import de.intevation.flys.artifacts.model.WQKms;
+
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.utils.StringUtil;
+
+
+public class WDifferencesState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(WDifferencesState.class);
+
+
+    public WDifferencesState() {
+    }
+
+
+    /** Specify to display nothing (this is kind of a "final" state). */
+    @Override
+    protected String getUIProvider() {
+        return "noinput";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = flys.getData("diffids");
+
+        if (data == null) {
+            throw new IllegalArgumentException("diffids is empty");
+        }
+
+        // TODO: Also validate format.
+
+        return true;
+    }
+
+
+    protected WKms getWKms(String mingle, CallContext context) {
+        String[] def  = mingle.split(";");
+        String   uuid = def[0];
+        String   name = def[1];
+        int      idx  = Integer.parseInt(def[2]);
+
+        if (name.startsWith("staticwkms")) {
+            StaticWKmsArtifact staticWKms =
+                (StaticWKmsArtifact) FLYSUtils.getArtifact(
+                    uuid,
+                    context);
+            logger.debug("WDifferencesState obtain data from StaticWKms");
+            WKms wkms = staticWKms.getWKms(idx);
+            if (wkms == null)
+                logger.error("No WKms from artifact.");
+            return wkms;
+        }
+    
+        WINFOArtifact flys = (WINFOArtifact) FLYSUtils.getArtifact(
+            uuid,
+            context);
+
+        if (flys == null) {
+            logger.warn("One of the artifacts (1) for diff calculation could not be loaded");
+            return null;
+        }
+        else{
+            WQKms[] wqkms = (WQKms[]) flys.getWaterlevelData().
+                                              getData();
+            if (wqkms == null)
+            logger.warn("not  waterlevels in artifact");
+            else if (wqkms.length < idx)
+            logger.warn("not enough waterlevels in artifact");
+            return wqkms[idx];
+        }
+    }
+
+
+    /**
+     * Return CalculationResult with Array of WKms that are difference of
+     * Waterlevels. Add respective facets (DifferencesCurveFacet, DataFacet).
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        String id = getID();
+
+        // Load the Artifacts/facets that we want to subtract and display.
+        // Expected format is:
+        // [42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;0]#[1231f2-....]
+        String diffids = winfo.getDataAsString("diffids");
+        logger.debug("WDifferencesState has: " + diffids);
+        String datas[] = diffids.split("#");
+
+        // Validate the Data-Strings.
+        for (String s: datas) {
+            if (!WaterlevelSelectState.isValueValid(winfo.getDataAsString("diffids"))) {
+                // TODO: escalate.
+            }
+        }
+
+        if (datas.length < 2) {
+            // TODO crash with style
+            ;
+        }
+
+        List<WKms> wkmss = new ArrayList<WKms>();
+
+        for(int i = 0; i < datas.length; i+=2) {
+            // e.g.:
+            // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1
+            WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]),
+                context);
+            WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]),
+                context);
+
+            String facetName = "diff ()";
+
+            if (minuendWKms != null && subtrahendWKms != null) {
+                facetName = StringUtil.wWrap(minuendWKms.getName()) 
+                    + " - " + StringUtil.wWrap(subtrahendWKms.getName());
+                WKms wkms = WKmsOperation.SUBTRACTION.operate(minuendWKms,
+                     subtrahendWKms);
+                wkms.setName(facetName);
+                wkmss.add(wkms);
+                logger.debug("WKMSSubtraction happened");
+            }
+    
+            if (facets != null) {
+                facets.add(new DifferenceCurveFacet(i/2, W_DIFFERENCES, facetName,
+                    ComputeType.ADVANCE, id, hash));
+                facets.add(new DataFacet(CSV, "CSV data"));
+                logger.debug("Adding facets in WDifferencesState.");
+            }
+            else {
+                logger.debug("Not adding facets in WDifferencesState.");
+            }
+        }
+
+        // TODO Evaluate whether null is okay as reports.
+        WKms[] diffs = wkmss.toArray(new WKms[wkmss.size()]);
+        CalculationResult result = new CalculationResult(diffs, null);
+        return result;
+    }
+}
+// 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/WMSBackgroundState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,120 @@
+package de.intevation.flys.artifacts.states;
+
+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.Node;
+
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.utils.Config;
+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.WMSLayerFacet;
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+
+public class WMSBackgroundState extends OutputState {
+
+    public static final String I18N_DESCRIPTION = "floodmap.wmsbackground";
+
+    public static final String XPATH_SRID =
+        "/artifact-database/floodmap/river[@name=$name]/srid/@value";
+
+    public static final String XPATH_WMS_URL =
+        "/artifact-database/floodmap/river[@name=$name]/background-wms/@url";
+
+    public static final String XPATH_WMS_LAYER =
+        "/artifact-database/floodmap/river[@name=$name]/background-wms/@layers";
+
+
+    protected String url;
+    protected String layer;
+    protected String srid;
+
+
+    private static final Logger logger = Logger.getLogger(WMSBackgroundState.class);
+
+
+    @Override
+    public void setup(Node config) {
+        super.setup(config);
+
+        logger.debug("WMSBackgroundState.setup()");
+    }
+
+
+    @Override
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        logger.debug("WMSBackgroundState.computeInit()");
+
+        if (url == null || layer == null) {
+            Document cfg = Config.getConfig();
+
+            String river = artifact.getDataAsString("river");
+
+            Map<String, String> variables = new HashMap<String, String>();
+            variables.put("name", river);
+
+            srid = (String) XMLUtils.xpath(
+                cfg,
+                XPATH_SRID,
+                XPathConstants.STRING,
+                null,
+                variables);
+
+            url = (String) XMLUtils.xpath(
+                cfg,
+                XPATH_WMS_URL,
+                XPathConstants.STRING,
+                null,
+                variables);
+
+            layer = (String) XMLUtils.xpath(
+                cfg,
+                XPATH_WMS_LAYER,
+                XPathConstants.STRING,
+                null,
+                variables);
+        }
+
+        if (url == null || layer == null) {
+            logger.warn("No background layers currently configured:");
+            logger.warn("... add config for WMS url: " + XPATH_WMS_URL);
+            logger.warn("... add config for WMS layer: " + XPATH_WMS_LAYER);
+            return null;
+        }
+
+        WMSLayerFacet facet = new WMSLayerFacet(
+            0,
+            FLOODMAP_WMSBACKGROUND,
+            Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION),
+            ComputeType.INIT,
+            getID(), hash,
+            url);
+
+        facet.addLayer(layer);
+        facet.setSrid(srid);
+
+        facets.add(facet);
+
+        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-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,456 @@
+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.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.RangeWithValues;
+import de.intevation.flys.artifacts.model.WstFactory;
+import de.intevation.flys.utils.FLYSUtils;
+
+
+/**
+ * @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");
+
+        WINFOArtifact flysArtifact = (WINFOArtifact) artifact;
+
+        double[]    dist   = FLYSUtils.getKmRange(flysArtifact);
+        River       river  = FLYSUtils.getRiver(flysArtifact);
+        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)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validate");
+
+        WINFOArtifact flys = (WINFOArtifact) 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);
+        }
+        else if (mode != null && mode.equals("Q")) {
+            return validateQ(artifact);
+        }
+        else {
+            throw new IllegalArgumentException("error_feed_no_wq_mode_selected");
+        }
+    }
+
+
+    protected boolean validateW(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validateW");
+        WINFOArtifact flys = (WINFOArtifact) artifact;
+
+        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        List<Gauge>     gauges = ((WINFOArtifact) 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)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validateQ");
+        WINFOArtifact flys = (WINFOArtifact) artifact;
+
+        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        List<Gauge> gauges = flys.getGauges();
+        River        river = FLYSUtils.getRiver(flys);
+        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:51 2012 +0200
@@ -0,0 +1,476 @@
+package de.intevation.flys.artifacts.states;
+
+import java.text.NumberFormat;
+
+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.artifacts.common.utils.XMLUtils.ElementCreator;
+
+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.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.WstFactory;
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQSelect extends DefaultState {
+
+    /** 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";
+
+    /** Them name fo the 'free' field. */
+    public static final String WQ_FREE = "wq_free";
+
+    /** 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() {
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        if (!name.equals(WQ_SINGLE)) {
+            return super.createStaticData(flys, creator, cc, name, value, type);
+        }
+
+        String mode = flys.getDataAsString(WQ_MODE);
+        String free = flys.getDataAsString(WQ_FREE);
+        if (mode == null || mode.equals("W") || Boolean.valueOf(free))  {
+            return super.createStaticData(flys, creator, cc, name, value, type);
+        }
+
+        WINFOArtifact winfo = (WINFOArtifact) flys;
+
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+        creator.addAttr(itemElement, "label", getLabel(winfo, cc, value), true);
+
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    protected static String getLabel(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        raw
+    ) {
+        String[] values = raw.split(" ");
+        String   label  = null;
+
+        NumberFormat nf = NumberFormat.getInstance(
+            Resources.getLocale(cc.getMeta()));
+
+        for (String value: values) {
+            try {
+                double v = Double.valueOf(value.trim());
+
+                String tmp = nf.format(v);
+                String mv  = FLYSUtils.getNamedMainValue(winfo.getGauge(),v);
+
+                if (mv != null && mv.length() > 0) {
+                    String add = mv + ": " + tmp;
+                    label = label != null ? label + ", " + add : add;
+                }
+                else {
+                    label = label != null ? label + ", " + tmp : tmp;
+                }
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing here
+            }
+        }
+
+        return label;
+    }
+
+
+    @Override
+    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   = ((WINFOArtifact) 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");
+
+        WINFOArtifact flysArtifact = (WINFOArtifact) artifact;
+
+        River river = FLYSUtils.getRiver(flysArtifact);
+        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)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQSelect.validate");
+
+        WINFOArtifact flys = (WINFOArtifact) artifact;
+
+        StateData data       = getData(flys, WQ_SELECTION);
+        String selectionMode = data != null ? (String) data.getValue() : null;
+
+        if (selectionMode == null || selectionMode.equals("single")) {
+            return validateSingle(artifact);
+        }
+        else {
+            return validateRange(artifact);
+        }
+    }
+
+
+    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");
+        }
+
+        return true;
+    }
+
+
+    protected boolean validateSingle(Artifact artifact)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateSingle");
+
+        WINFOArtifact flys = (WINFOArtifact) 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)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateRange");
+        WINFOArtifact flys = (WINFOArtifact) 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, from, to, step);
+            }
+            else if (mode != null && mode.trim().toLowerCase().equals("q")) {
+                return validateQ(artifact, 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 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,
+        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 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,
+        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/WaterlevelGroundDifferences.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,126 @@
+package de.intevation.flys.artifacts.states;
+
+import org.w3c.dom.Element;
+
+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.ProtocolUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WaterlevelGroundDifferences extends RangeState {
+
+    public static final String LOWER_FIELD  = "diff_from";
+    public static final String UPPER_FIELD  = "diff_to";
+    public static final String DIFF_FIELD   = "diff_diff";
+
+    public static final double DEFAULT_STEP = 0d;
+
+
+    private static Logger logger =
+        Logger.getLogger(WaterlevelGroundDifferences.class);
+
+
+
+    @Override
+    protected String getLowerField() {
+        return LOWER_FIELD;
+    }
+
+
+    @Override
+    protected String getUpperField() {
+        return UPPER_FIELD;
+    }
+
+
+    @Override
+    protected String getStepField() {
+        return DIFF_FIELD;
+    }
+
+
+    @Override
+    protected double[] getMinMax(Artifact artifact) {
+        return new double[] { -Double.MAX_VALUE, Double.MAX_VALUE };
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "waterlevel_ground_panel";
+    }
+
+
+    protected double getDefaultStep() {
+        return DEFAULT_STEP;
+    }
+
+
+    @Override
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        double[] minmax = getMinMax(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(LOWER_FIELD)) {
+            Element min = createItem(
+                cr,
+                new String[] {"min", new Double(minVal).toString()});
+
+            return new Element[] { min };
+        }
+        else if (name.equals(UPPER_FIELD)) {
+            Element max = createItem(
+                cr,
+                new String[] {"max", new Double(maxVal).toString()});
+
+            return new Element[] { max };
+        }
+        else {
+            Element step = createItem(
+                cr,
+                new String[] {"step", String.valueOf(getDefaultStep())});
+            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;
+    }
+}
+// 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/WaterlevelInfoState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,151 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+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.CrossSectionFacet;
+import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+
+public class WaterlevelInfoState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(WaterlevelInfoState.class);
+
+
+    @Override
+    protected String getUIProvider() {
+        return "noinput";
+    }
+
+
+    public Object computeInit(
+        FLYSArtifact artifact,
+        String       hash,
+        Object       context,
+        CallMeta     meta,
+        List<Facet>  facets
+    ) {
+        return compute((WINFOArtifact) artifact, hash, facets, null);
+    }
+
+
+    protected Object compute(
+        WINFOArtifact winfo,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        logger.debug("WaterlevelInfoState.compute");
+        String id = getID();
+
+        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("WaterlevelInfoState Create facet: " + nameW);
+            logger.debug("WaterlevelInfoState Create facet: " + nameQ);
+
+            Facet w = new WaterlevelFacet(
+                i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash);
+            Facet q = new WaterlevelFacet(
+                i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash);
+
+            facets.add(w);
+            facets.add(q);
+        }
+
+        if (wqkms.length > 0) {
+            Facet wst = new DataFacet(
+                WST, "WST data", ComputeType.ADVANCE, hash, id);
+            Facet csv = new DataFacet(
+                CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+
+            facets.add(wst);
+            facets.add(csv);
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        // Also register the CrossSectionFacets (added to respective out).
+        int idx = 0;
+        for (String name: winfo.getCrossSectionNames()) {
+            facets.add(new CrossSectionFacet(idx++, name));
+        }
+
+        // TODO Adjust to WaterlevelState - implementation.
+        facets.add(new CrossSectionWaterLineFacet(0, "Q=" + winfo.getDataAsString("wq_single")));
+
+        // Assume to be in wq_single mode.
+        return res;
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((WINFOArtifact) artifact, hash, facets, old);
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((WINFOArtifact) artifact, hash, facets, old);
+    }
+}
+// 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/WaterlevelPairSelectState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,162 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+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.state.Facet;
+
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.flys.utils.StringUtil;
+
+/**
+ * State in which the user selects 1 to n pairs of Waterlevels and alikes.
+ */
+public class WaterlevelPairSelectState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(
+         WaterlevelPairSelectState.class);
+
+
+    /** Trivial constructor. */
+    public WaterlevelPairSelectState() {
+    }
+
+
+    /** Specify to display a datacage_twin_panel. */
+    @Override
+    protected String getUIProvider() {
+        return "datacage_twin_panel";
+    }
+
+
+    /**
+     * Overridden to do nothing.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        //Get data and do stuff, do not calculate
+        return "";
+    }
+
+
+    /**
+     * Create elements for document (prepopulated with data, if any).
+     * @param artifact FLYSArtifact to get data from.
+     * @param name DataName, expceted to be "diffids".
+     */
+    @Override
+    protected Element[] createItems(
+        ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("createItems: " + name);
+        if (name.equals("diffids")) {
+            Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+            Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+            Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+            FLYSArtifact flys = (FLYSArtifact) artifact;
+            String s = flys.getDataAsString("diffids");
+            value.setTextContent(s);
+            item.appendChild(label);
+            item.appendChild(value);
+            return new Element[] { item };
+        }
+        return new Element[] {};
+    }
+
+
+    /**
+     * Creats the data element used for the static part of DESCRIBE document.
+     */
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        String[] labels = getLabels(cc, value);
+        Object[] obj    = new Object[] { labels[0] };
+
+        // TODO own i18n
+        String attrValue = Resources.getMsg(
+            cc.getMeta(), "wsp.selected.string", "wsp.selected.string", obj);
+        //I18N_STATIC_KEY, I18N_STATIC_KEY, obj);
+
+        creator.addAttr(itemElement, "label", attrValue, true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    /**
+     * Get name to display for selected watelerlevels (for example "Q=123")
+     * from the CalculationResult. 
+     */
+    public static String[] getLabels(CallContext cc, String value) {
+        String[] recommendations = value.split("#");
+        String displayString = "";
+
+        // Walk over all selected recommendations and create label
+        // like "W (Q=1) - W (Q=2)".
+        for (int i = 0; i < recommendations.length; i+=2) {
+            String[] minuendParts = StringUtil
+                .unbracket(recommendations[i+0])
+                .split(";");
+            if(minuendParts.length >= 4) {
+                displayString += "(" + minuendParts[3];
+            }
+            else {
+                displayString += "([error]";
+            }
+
+            displayString += " - ";
+
+            String[] subtrahendParts = StringUtil
+                .unbracket(recommendations[i+1])
+                .split(";");
+            if(subtrahendParts.length >= 4) {
+                displayString += subtrahendParts[3] + ") ";
+            }
+            else {
+                displayString += "[error])";
+            }
+        }
+
+        return new String[] { displayString };
+    }
+}
+// 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/WaterlevelSelectState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,183 @@
+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.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.utils.StringUtil;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WaterlevelSelectState extends DefaultState {
+
+    private static final Logger logger =
+        Logger.getLogger(WaterlevelSelectState.class);
+
+    public static final String SPLIT_CHAR = ";";
+
+    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
+
+    public static final String I18N_STATIC_KEY = "wsp.selected.string";
+
+
+    @Override
+    protected String getUIProvider() {
+        return "wsp_datacage_panel";
+    }
+
+
+    /**
+     * @param flys ignored
+     * @param cc   ignrored
+     */
+    @Override
+    public StateData transform(
+        FLYSArtifact flys,
+        CallContext  cc,
+        String       name,
+        String       val
+    ) {
+        if (!isValueValid(val)) {
+            logger.error("The given input string is not valid: '" + val + "'");
+            return null;
+        }
+
+        return new DefaultStateData(name, null, null, StringUtil.unbracket(val));
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact)
+    throws IllegalArgumentException
+    {
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = flys.getData("wsp");
+
+        if (data == null) {
+            throw new IllegalArgumentException("WSP is empty");
+        }
+
+        return true;
+    }
+
+
+    @Override
+    protected Element createStaticData(
+        FLYSArtifact   flys,
+        ElementCreator creator,
+        CallContext    cc,
+        String         name,
+        String         value,
+        String         type
+    ) {
+        Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        String[] labels = getLabels(cc, value);
+        Object[] obj    = new Object[] { labels[0] };
+
+        String attrValue = Resources.getMsg(
+            cc.getMeta(), I18N_STATIC_KEY, I18N_STATIC_KEY, obj);
+
+        creator.addAttr(itemElement, "label", attrValue, true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+
+
+    /**
+     * Get name to display for selected watelerlevel (for example "Q=123")
+     * from the CalculationResult. 
+     */
+    public static String[] getLabels(CallContext cc, String value) {
+        String[] parts = value.split(SPLIT_CHAR);
+
+        FLYSArtifact artifact = FLYSUtils.getArtifact(parts[0], cc);
+
+        CalculationResult rawData = (CalculationResult) artifact.compute(
+            cc,
+            null,
+            WINFO_WSP_STATE_ID,
+            ComputeType.ADVANCE,
+            false);
+
+        WQKms[] wqkms = (WQKms[]) rawData.getData();
+
+        int idx = -1;
+        try {
+            idx = Integer.parseInt(parts[2]);
+        }
+        catch (NumberFormatException nfe) { /* do nothing */ }
+
+        String name = wqkms[idx].getName();
+
+        return new String[] { StringUtil.wWrap(name) };
+    }
+
+
+    /**
+     * Validates the given String. A valid string for this state requires the
+     * format: "UUID;FACETNAME;FACETINDEX".
+     *
+     * @param value The string value requires validation.
+     *
+     * @return true, if the string applies the specified format, otherwise
+     * false.
+     */
+    public static boolean isValueValid(String value) {
+        logger.debug("Validate string: '" + value + "'");
+
+        value = StringUtil.unbracket(value);
+
+        logger.debug("Validate substring: '" + value + "'");
+
+        if (value == null || value.length() == 0) {
+            return false;
+        }
+
+        String[] parts = value.split(SPLIT_CHAR);
+
+        if (parts == null || parts.length < 3) {
+            return false;
+        }
+
+        if (parts[0] == null || parts[0].length() == 0) {
+            return false;
+        }
+
+        if (parts[1] == null || parts[1].length() == 0) {
+            return false;
+        }
+
+        try {
+            Integer.parseInt(parts[2]);
+        }
+        catch (NumberFormatException nfe) {
+            logger.error("Index is not a valid integer!", nfe);
+        }
+
+        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/WaterlevelState.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,133 @@
+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.DataFacet;
+import de.intevation.flys.artifacts.model.CrossSectionFacet;
+import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
+import de.intevation.flys.artifacts.model.CalculationResult;
+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.utils.FLYSUtils;
+
+
+public class WaterlevelState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state. */
+    private static Logger logger = Logger.getLogger(WaterlevelState.class);
+
+
+    /**
+     * From this state can only be continued trivially.
+     */
+    @Override
+    protected String getUIProvider() {
+        return "continue";
+    }
+
+
+    protected Object compute(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        hash,
+        List<Facet>   facets,
+        Object        old
+    ) {
+        String id = getID();
+
+        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  name = wqkms[i].getName();
+
+            String nameW = FLYSUtils.createWspWTitle(winfo, cc, name);
+            String nameQ = FLYSUtils.createWspQTitle(winfo, cc, name);
+
+            logger.debug("Create facet: " + nameW);
+            logger.debug("Create facet: " + nameQ);
+
+            Facet w = new WaterlevelFacet(
+                i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash);
+            Facet q = new WaterlevelFacet(
+                i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash);
+
+            facets.add(new CrossSectionWaterLineFacet(i, nameQ));
+
+            facets.add(w);
+            facets.add(q);
+        }
+
+        if (wqkms.length > 0) {
+            Facet wst = new DataFacet(
+                WST, "WST data", ComputeType.ADVANCE, hash, id);
+            Facet csv = new DataFacet(
+                CSV, "CSV data", ComputeType.ADVANCE, hash, id);
+
+            facets.add(wst);
+            facets.add(csv);
+            // Also register the CrossSectionFacets.
+            int idx = 0;
+            for (String name: winfo.getCrossSectionNames()) {
+                facets.add(new CrossSectionFacet(idx++, name));
+            }
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
+        }
+
+        return res;
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((WINFOArtifact) artifact, context, hash, facets, old);
+
+    }
+
+
+    /**
+     * @param context Ignored.
+     */
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return compute((WINFOArtifact) artifact, context, hash, facets, old);
+    }
+}
+// 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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,134 @@
+package de.intevation.flys.collections;
+
+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.ArtifactNamespaceContext;
+
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.Output;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.model.ManagedFacet;
+import de.intevation.flys.artifacts.model.ManagedDomFacet;
+
+/**
+ * Access parts of the Attribute parts of a FLYSCollections description
+ * document.
+ */
+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;
+
+    /** List of facets. */
+    protected List<Facet> facets;
+
+
+    public AttributeParser() {
+        this.outs   = new HashMap<String, Output>();
+        this.facets = new ArrayList<Facet>();
+    }
+
+
+    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;
+    }
+
+
+    /**
+     * Adds item (a ManagedFacet) to an out.
+     */
+    protected void addItem(String out, ManagedFacet item) {
+        this.facets.add(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);
+    }
+
+
+    /**
+     * Access all facets.
+     * @return list of all facets.
+     */
+    public List<Facet> getFacets() {
+        return this.facets;
+    }
+
+
+    protected void parseItems(Node out, String outname) {
+        NodeList themes = (NodeList) XMLUtils.xpath(
+            out, "art:facet",
+            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);
+
+            addItem(outname, new ManagedDomFacet(theme));
+        }
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,294 @@
+package de.intevation.flys.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+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.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+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.FLYSArtifact;
+import de.intevation.flys.artifacts.model.ManagedFacet;
+
+/**
+ * Create attribute- element of describe document of an ArtifactCollection.
+ * The attribute-element contains the merged output of all outputmodes and
+ * facets that are part of the collection.
+ */
+public class AttributeWriter {
+
+    /** ArtifactDatabase used to fetch Artifacts. */
+    protected ArtifactDatabase db = null;
+
+    protected Map<String, Output> oldAttr;
+
+    protected Map<String, Output> newAttr;
+
+    /** List of already seen facets. */
+    protected List<Facet>         oldFacets;
+
+    /** List of "new" facets. */
+    protected List<Facet>         newFacets;
+
+    /**
+     * "Compatibility matrix", mapws list of facet names to output names.
+     * Any facet that is not found in the list for a specific output will
+     * not be added to the resulting document.
+     */
+    protected Map<String, List<String>> compatibilities;
+
+    private static Logger logger = Logger.getLogger(AttributeWriter.class);
+
+
+    /**
+     * Create a AttributeWriter.
+     * Attributes not present in newAttr will not be included in the document.
+     * @param db      Database to fetch artifacts.
+     * @param oldAttr "Old" (possibly user-changed) outputs.
+     * @param newAttr "New" (eventually re-read in its original, unchanged
+     *                form) outputs.
+     */
+    public AttributeWriter(
+        ArtifactDatabase    db,
+        Map<String, Output> oldAttr,
+        List<Facet>         oldFacets,
+        Map<String, Output> newAttr,
+        List<Facet>         newFacets,
+        Map<String, List<String>> matrix)
+    {
+        this.db        = db;
+        this.oldAttr   = oldAttr;
+        this.newAttr   = newAttr;
+        this.oldFacets = oldFacets;
+        this.newFacets = newFacets;
+        this.compatibilities = matrix;
+    }
+
+
+    /**
+     * Create document by merging outputs given in
+     * constructor.
+     *
+     * The "new" set rules about existance of attributes, so anything not
+     * present in it will not be included in the resulting document.
+     * The "old" set rules about the content of attributes (as user changes
+     * are recorded here and not in the new set).
+     *
+     * @return document with merged outputs as described.
+     */
+    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);
+
+            writeOutput(doc, outs, cr, a.getName(), newFacets, oldFacets);
+        }
+
+        return doc;
+    }
+
+
+    /**
+     * @param doc  Document to add output nodes to
+     * @param outs Node in Document to add output nodes to
+     * @param cr   ElementCreator in use to modify doc/outs
+     * @param outputName the "new" outputs name
+     * @param newOutFacets Facets of the new outputs
+     * @param oldOutFacets Facets of the old outputs (can be null)
+     */
+    protected void writeOutput(
+        Document       doc,
+        Node           outs,
+        ElementCreator cr,
+        String         outputName,
+        List<Facet>    newOutFacets,
+        List<Facet>    oldOutFacets)
+    {
+        Element output = cr.create("output");
+        cr.addAttr(output, "name", outputName);
+
+        outs.appendChild(output);
+
+        List<String> compatibleFacets = this.compatibilities.get(outputName);
+        try {
+            writeFacets(doc, cr, output, newOutFacets, oldOutFacets, compatibleFacets);
+        }
+        catch (ArtifactDatabaseException ade) {
+            logger.error(ade, ade);
+        }
+    }
+
+
+    /**
+     * @param doc    Document to add facet nodes to
+     * @param cr     ElementCreator to use with output/doc
+     * @param output Node in Document to add facet nodes to
+     * @param newFacets the new facets
+     * @param oldFacets the old facets
+     * @param compatibleFacets List of facets to accept
+     */
+    protected void writeFacets(
+        Document       doc,
+        ElementCreator cr,
+        Element        output,
+        List<Facet>    newFacets,
+        List<Facet>    oldFacets,
+        List<String>   compatibleFacets)
+    throws ArtifactDatabaseException
+    {
+        if (compatibleFacets == null) {
+            logger.warn("No compatible facets, not generating out.");
+            return;
+        }
+
+        int num = newFacets.size();
+
+        // Add all new Facets either in their old state or (if really
+        // new) as they are.
+        List<ManagedFacet> currentFacets      = new ArrayList<ManagedFacet>();
+        List<ManagedFacet> genuinelyNewFacets = new ArrayList<ManagedFacet>();
+
+        for (int i = 0; i < num; i++) {
+            ManagedFacet facet = (ManagedFacet) newFacets.get(i);
+            if (!compatibleFacets.contains(facet.getName())) {
+                //logger.debug("Have incompatible facet, skip: " + facet.getName());
+                continue;
+            }
+            //else logger.debug("Have compatible facet: " + facet.getName());
+
+            ManagedFacet picked = pickFacet(facet, oldFacets);
+
+            if (facet.equals(picked)) {
+                genuinelyNewFacets.add(picked);
+            }
+            else {
+                currentFacets.add(picked);
+            }
+        }
+
+        // With each genuinely new Facet, ask Artifact whether it comes to live
+        // in/activate.
+        for (ManagedFacet newMF: genuinelyNewFacets) {
+            FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(newMF.getArtifact());
+            newMF.setActive(flys.getInitialFacetActivity(
+                output.getAttribute("name"),
+                newMF.getName(),
+                newMF.getIndex()));
+        }
+        
+        // For each genuinely new Facet check positional conflicts.
+        for (ManagedFacet newMF: genuinelyNewFacets) {
+            boolean conflicts = true;
+            // Loop until all conflicts resolved.
+            while (conflicts) {
+                conflicts = false;
+                for (ManagedFacet oldMF: currentFacets) {
+                    if (newMF.getPosition() == oldMF.getPosition()) {
+                        conflicts = true;
+                        logger.debug("Positional conflict while merging " +
+                            "facets, pushing newest facet 1 up (" + newMF.getPosition() + ")");
+                        newMF.setPosition(newMF.getPosition() + 1);
+                        break;
+                    }
+                }
+            }
+            currentFacets.add(newMF);
+        }
+
+        // Fill/correct "gaps" (e.g. position 1,2,5 are taken, after gap filling
+        // expect positions 1,2,3 [5->3])
+        // Preparations to be able to detect gaps.
+        Map<Integer, ManagedFacet> mfmap = new HashMap<Integer, ManagedFacet>();
+        int max = 0;
+        for (ManagedFacet mf: currentFacets) {
+            int pos = mf.getPosition();
+            mfmap.put(Integer.valueOf(pos), mf);
+            if (pos > max) max = pos;
+        }
+
+        // Finally do gap correction.
+        if (max != currentFacets.size()) {
+            int gap = 0;
+            for (int i = 1; i <= max; i++) {
+                ManagedFacet mf = mfmap.get(Integer.valueOf(i));
+                if (mf == null) {
+                    gap++;
+                    continue;
+                }
+                mf.setPosition(mf.getPosition() - gap);
+            }
+        }
+
+        // Now add all facets.
+        for (ManagedFacet oldMF: currentFacets) {
+            Node node = oldMF.toXML(doc);
+            if (node != null) {
+                output.appendChild(node);
+            }
+        }
+    }
+
+
+    /**
+     * Returns the facet to be added to Document.
+     * Return the new facet only if the "same" facet was not present before.
+     * Return the "old" facet otherwise (user-defined information sticks
+     * to it).
+     * @param facet     the new facet.
+     * @param oldFacets the old facets, new facet is compared against each of
+     *                  these.
+     * @return facet if genuinely new, matching old facet otherwise.
+     */
+    protected ManagedFacet pickFacet(ManagedFacet facet,
+        List<Facet> oldFacets)
+    {
+        if (oldFacets == null) {
+            logger.debug("No old facets to compare a new to found.");
+            return facet;
+        }
+       
+        String hash = facet.getName() + facet.getIndex() + facet.getArtifact();
+
+        // Compare "new" facet with all old facets.
+        // Take oldFacet if that facet was already present (otherwise
+        // information is lost, the new one otherwise.
+        for (Facet oFacet: oldFacets) {
+            ManagedFacet oldFacet = (ManagedFacet) oFacet;
+            String oldHash = oldFacet.getName()
+                           + oldFacet.getIndex()
+                           + oldFacet.getArtifact();
+            if (hash.equals(oldHash)) {
+                return oldFacet;
+            }
+        }
+        return facet;
+    }
+}
+// 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:51 2012 +0200
@@ -0,0 +1,873 @@
+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.artifactdatabase.state.StateEngine;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.ManagedFacet;
+import de.intevation.flys.artifacts.model.ManagedDomFacet;
+import de.intevation.flys.exports.OutGenerator;
+import de.intevation.flys.themes.Theme;
+import de.intevation.flys.themes.ThemeFactory;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+/**
+ * @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";
+
+    /** Xpath to master artifacts uuid. */
+    public static final String XPATH_MASTER_UUID =
+        "/art:artifact-collection/art:artifact/@art:uuid";
+
+    public static final String XPATH_LOADED_RECOMMENDATIONS =
+        "/art:attribute/art:loaded-recommendations";
+
+
+    /**
+     * Return description Document for this collection.
+     */
+    @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();
+        Document oldAttrs   = getAttribute();
+
+        try {
+            String[] aUUIDs = getArtifactUUIDs(context);
+            Node newAttr    = mergeAttributes(db, context, oldAttrs, aUUIDs);
+
+            collection.appendChild(doc.importNode(newAttr, true));
+
+            // Make it an empty array if null.
+            if (aUUIDs == null) {
+                aUUIDs = new String[] {};
+            }
+
+            for (String uuid: aUUIDs) {
+                try {
+                    artifacts.appendChild(
+                        buildArtifactNode(db, uuid, context, ec));
+                }
+                catch (ArtifactDatabaseException dbe) {
+                    log.warn(dbe, dbe);
+                }
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+
+            collection.appendChild(
+                doc.importNode(oldAttrs.getFirstChild(), true));
+        }
+
+        return doc;
+    }
+
+
+    /**
+     * Merge the current art:outputs nodes with the the outputs provided by the
+     * artifacts in the Collection.
+     *
+     * @param uuids Artifact uuids.
+     */
+    protected Node mergeAttributes(
+        ArtifactDatabase db,
+        CallContext      context,
+        Document         oldAttrs,
+        String[]         uuids)
+    {
+        Document doc = buildOutAttributes(db, context, oldAttrs, uuids);
+        Node newAttr = doc.getFirstChild();
+
+        newAttr = mergeLoadedRecommendations(oldAttrs, newAttr);
+
+        try {
+            // Save the merged document into database.
+            db.setCollectionAttribute(identifier(), context.getMeta(), doc);
+        }
+        catch (ArtifactDatabaseException adb) {
+            log.error(adb, adb);
+        }
+
+        return newAttr;
+    }
+
+
+    /**
+     * Merge the recommendations which have already been loaded from the old
+     * attribute document into the new attribute document. This is necessary,
+     * because mergeAttributes() only merges the art:outputs nodes - all
+     * other nodes are skiped.
+     */
+    protected Node mergeLoadedRecommendations(Document oldAttrs, Node newAttrs){
+        Element loadedRecoms = (Element) XMLUtils.xpath(
+            oldAttrs,
+            XPATH_LOADED_RECOMMENDATIONS,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (loadedRecoms != null) {
+            Document doc = newAttrs.getOwnerDocument();
+            newAttrs.appendChild(doc.importNode(loadedRecoms, true));
+        }
+
+        return newAttrs;
+    }
+
+
+    @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
+             && type.indexOf("chartinfo") > 0)
+        {
+            generator = getOutGenerator(context, type, 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);
+
+        // Get master artifact.
+        FLYSArtifact master = getMasterArtifact(context);
+        if (master != null) {
+            log.debug("Will set master Artifact to uuid: " + master.identifier());
+            generator.setMasterArtifact(master);
+        }
+        else {
+            log.warn("Could not set master artifact.");
+        }
+
+        try {
+            Document attr = getAttribute(context, name);
+            doOut(generator, name, subtype, attr, context);
+        }
+        catch (ArtifactDatabaseException adbe) {
+            log.error(adbe, adbe);
+        }
+    }
+
+
+    /**
+     * 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;
+                }
+
+                Artifact artifact = getArtifact(art, context);
+
+                generator.doOut(
+                    artifact,
+                    theme,
+                    getFacetThemeFromAttribute(
+                        art,
+                        outName,
+                        facetName,
+                        theme.getDescription(),
+                        theme.getIndex(),
+                        context),
+                    theme.getActive() == 1);
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+        }
+
+        generator.generate();
+    }
+
+
+    /**
+     * @return masterartifact or null if exception/not found.
+     */
+    protected FLYSArtifact getMasterArtifact(CallContext context)
+    {
+        try {
+            ArtifactDatabase db = context.getDatabase();
+            CallMeta callMeta   = context.getMeta();
+            Document document   = db.getCollectionsMasterArtifact(
+                identifier(), callMeta);
+    
+            String masterUUID   = XMLUtils.xpathString(
+                document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE);
+            FLYSArtifact masterArtifact =
+                (FLYSArtifact) getArtifact(masterUUID, context);
+            return masterArtifact;
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+        }
+        return null;
+    }
+
+
+    /**
+     * Return merged output document.
+     * @param uuids List of artifact uuids.
+     */
+    protected Document buildOutAttributes(
+        ArtifactDatabase db,
+        CallContext      context,
+        Document         oldAttr,
+        String[]         uuids)
+    {
+        Document doc = XMLUtils.newDocument();
+
+        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
+        StateEngine engine = (StateEngine) flysContext.get(
+        FLYSContext.STATE_ENGINE_KEY);
+
+        FLYSArtifact masterArtifact = getMasterArtifact(context);
+    
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+    
+        AttributeParser aParser = new AttributeParser();
+        OutputParser    oParser = new OutputParser(db, context);
+    
+        if (uuids != null) {
+            for (String uuid: uuids) {
+                try {
+                    oParser.parse(uuid);
+                }
+                catch (ArtifactDatabaseException ade) {
+                    log.warn(ade, ade);
+                }
+            }
+        }
+    
+        aParser.parse(oldAttr);
+    
+        return new AttributeWriter(
+            db,
+            aParser.getOuts(),
+            aParser.getFacets(),
+            oParser.getOuts(),
+            oParser.getFacets(),
+            engine.getCompatibleFacets(masterArtifact.getStateHistoryIds())
+            ).write();
+    }
+
+
+    /**
+     * Returns the "attribute" (part of description document) 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 = buildOutAttributes(
+            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 and facet 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,
+        String      pattern,
+        int         index,
+        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();
+
+        Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta);
+
+        if (attr == null) {
+            attr = initItemAttribute(uuid, facet, pattern, index, outName, 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 for facet '" + facet + "' in attribute.");
+
+        Node theme = (Node) XMLUtils.xpath(
+            tmp,
+            "art:themes/theme[@facet='" + facet +
+            "' and @index='" + String.valueOf(index) + "']",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (theme == null) {
+            log.warn("Could not find the theme in attribute of: " + uuid);
+
+            Theme t = getThemeForFacet(
+                uuid, facet, pattern, index, outName, 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,
+        String      pattern,
+        int         index,
+        String      outName,
+        CallContext context)
+    {
+        log.info("FLYSArtifactCollection.initItemAttribute");
+
+        Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, 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,
+        String pattern,
+        int    index,
+        String outName,
+        CallContext context)
+    {
+        log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
+
+        FLYSContext flysContext = context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) context.globalContext();
+
+        // Push artifact in flysContext.
+        ArtifactDatabase db = context.getDatabase();
+        try {
+            FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid);
+            log.debug("Got raw artifact");
+            flysContext.put(flysContext.ARTIFACT_KEY, artifact);
+        }
+        catch (Exception e) {
+            log.error("Exception caught when trying to get art.", e);
+        }
+
+        Theme t = ThemeFactory.getTheme(flysContext, facet, pattern, outName);
+
+        if (t != null) {
+            t.setFacet(facet);
+            t.setIndex(index);
+        }
+
+        return t;
+    }
+
+
+    /**
+     * 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 Instance of an OutGenerator for specified <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 static 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 themeList = (NodeList) XMLUtils.xpath(
+                output,
+                "art:output/art:facet",
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            int num = themeList != null ? themeList.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) themeList.item(i);
+
+                ManagedDomFacet facet = new ManagedDomFacet(theme);
+                themes.put(Integer.valueOf(facet.getPosition()-1), facet);
+            }
+        }
+
+        public ManagedFacet get(int idx) {
+            return themes.get(Integer.valueOf(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:51 2012 +0200
@@ -0,0 +1,130 @@
+package de.intevation.flys.collections;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.Output;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.ManagedFacetAdapter;
+
+
+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 CallContext      context;
+
+    /** Map outputs name to Output. */
+    protected Map<String, Output> outs;
+
+    /** Map facets name to list of Facets. */
+    protected List<Facet> facets;
+
+
+    /**
+     * @param db Database used to fetch artifacts, outputs and facets.
+     */
+    public OutputParser(ArtifactDatabase db, CallContext context) {
+        this.db      = db;
+        this.meta    = context.getMeta();
+        this.context = context;
+        this.outs    = new HashMap<String, Output>();
+        this.facets  = new ArrayList<Facet>();
+    }
+
+
+    /**
+     * Gets raw artifact with given id and sorts outputs in mapping.
+     * Converts Facets to ManagedFacets on the way.
+     * @param uuid uuid of artifact to load from database.
+     */
+    public void parse(String uuid)
+    throws ArtifactDatabaseException
+    {
+        logger.debug("OutputParser.parse: " + uuid);
+
+        FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(uuid);
+
+        List<Output> outList = flys.getOutputs(context);
+
+        for (Output out: outList) {
+            String name = out.getName();
+
+            Output o = outs.get(name);
+            int  pos = 1;
+
+            if (o == null) {
+                o = new DefaultOutput(
+                    out.getName(),
+                    out.getDescription(),
+                    out.getMimeType(),
+                    new ArrayList<Facet>(),
+                    out.getType());
+                outs.put(name, o);
+            }
+            else {
+                logger.debug("OutputParser.parse: Use 'old' Output");
+                pos = o.getFacets().size() + 1;
+            }
+
+            List<Facet> mfacets = facet2ManagedFacet(uuid, out.getFacets(), pos);
+            o.addFacets(mfacets);
+            this.facets.addAll(mfacets);
+        }
+    }
+
+
+    /**
+     * Access mapping of Outputname to Output.
+     */
+    public Map<String, Output> getOuts() {
+        return outs;
+    }
+
+
+    /**
+     * Access all facets.
+     */
+    public List<Facet> getFacets() {
+        return this.facets;
+    }
+
+
+    /**
+     * Creates a list of ManagedFacets from list of Facets.
+     * @param pos Position of first facet (for each other the positions
+     *            will be increased).
+     */
+    protected List<Facet> facet2ManagedFacet(
+        String      uuid,
+        List<Facet> old,
+        int         pos)
+    {
+        List<Facet> newFacets = new ArrayList<Facet>(old.size());
+
+        for (Facet f: old) {
+            newFacets.add(new ManagedFacetAdapter(f, uuid, pos++, 1, 1));
+        }
+
+        return newFacets;
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,94 @@
+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;
+
+import de.intevation.flys.utils.FLYSUtils;
+
+
+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;
+    protected FLYSArtifact master;
+
+    public ATExporter() {
+    }
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        this.context = context;
+        this.out     = out;
+    }
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        this.master = (FLYSArtifact) master;
+    }
+
+    @Override
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible
+    ) {
+
+        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);
+        }
+
+        String   river = FLYSUtils.getRiver(master).getName();
+        double[] kms   = FLYSUtils.getLocations(master);
+
+        at.write(
+            new OutputStreamWriter(out, DEFAULT_ENCODING),
+            context.getMeta(),
+            river,
+            kms[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-artifacts/src/main/java/de/intevation/flys/exports/ATWriter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,166 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.io.PrintWriter;
+
+import java.util.Locale;
+
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.flys.artifacts.model.WQ;
+import de.intevation.flys.artifacts.resources.Resources;
+
+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 I18N_AT_HEADER =
+        "export.discharge.curve.at.header";
+
+    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 [] bounds = wq.longestIncreasingWRangeIndices();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("exporting w between indices " +
+                bounds[0] + " and " + bounds[1] + " (" +
+                wq.getW(bounds[0]) + ", " + wq.getW(bounds[1]));
+        }
+
+        if (bounds[1]-bounds[0] < 1) { // Only first w can be written out.
+            minW = maxW = wq.getW(bounds[0]);
+            minQ = maxQ = wq.getQ(bounds[0]);
+            // constant function
+            qFunc = new PolynomialFunction(new double [] { minQ });
+            return;
+        }
+
+        double [] ws = new double[bounds[1]-bounds[0]];
+        double [] qs = new double[ws.length];
+
+        for (int i = 0; i < ws.length; ++i) {
+            int idx = bounds[0]+i;
+            ws[i] = wq.getW(idx);
+            qs[i] = wq.getQ(idx);
+        }
+
+        qFunc = ws.length < 3
+            ? new LinearInterpolator().interpolate(ws, qs)
+            : new SplineInterpolator().interpolate(ws, qs);
+
+        minW = wq.getW(bounds[0]);
+        maxW = wq.getW(bounds[1]);
+        minQ = wq.getQ(bounds[0]);
+        maxQ = wq.getQ(bounds[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);
+    }
+
+
+    protected static void printHeader(
+        PrintWriter out,
+        CallMeta    callMeta,
+        String      river,
+        double      km
+    ) {
+        out.println(Resources.getMsg(
+            callMeta,
+            I18N_AT_HEADER,
+            I18N_AT_HEADER,
+            new Object[] { river, km } ));
+    }
+
+
+    public void write(Writer writer, CallMeta meta, String river, double km)
+    throws IOException
+    {
+        PrintWriter out = new PrintWriter(writer);
+
+        // a header is required, because the desktop version of FLYS will skip
+        // the first row.
+        printHeader(out, meta, river, km);
+
+        double rest = (minW * 100.0) % 10.0;
+
+        double startW = Math.rint((minW - rest*0.01)*10.0)*0.1;
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("startW: " + startW);
+            logger.debug("rest: " + rest);
+        }
+
+        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));
+            }
+
+            if (w < minW) {
+                out.print(EMPTY);
+            }
+            else {
+                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:51 2012 +0200
@@ -0,0 +1,232 @@
+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,
+        boolean  visible
+    ) {
+        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:51 2012 +0200
@@ -0,0 +1,336 @@
+/*
+ * 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 org.jfree.chart.ChartRenderingInfo;
+
+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 {
+
+    public static final String FORMAT_PNG = "png";
+
+    public static final String FORMAT_PDF = "pdf";
+
+    public static final String FORMAT_SVG = "svg";
+
+
+    /**
+     * Constant field to define A4 as default page size.
+     */
+    public static final String  DEFAULT_PAGE_SIZE = "A4";
+
+    /**
+     * Constant field to define UTF-8 as default encoding.
+     */
+    public 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,
+        CallContext  cc
+    )
+    throws IOException
+    {
+        log.info("export chart as png");
+
+        ChartRenderingInfo info = new ChartRenderingInfo();
+
+        String format = (String) cc.getContextValue("chart.image.format");
+
+        int[] size = getSize(cc);
+
+        ImageIO.write(
+            chart.createBufferedImage(
+                size[0], size[1], Transparency.BITMASK, info
+            ),
+            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 context The CallContext object that contains extra chart
+     * parameters.
+     */
+    public static void exportSVG(
+        OutputStream out,
+        JFreeChart   chart,
+        CallContext  context
+    ) {
+        String encoding = (String) context.getContextValue("chart.encoding");
+
+        log.info("export chart as svg");
+
+        if (encoding == null)
+            encoding = DEFAULT_ENCODING;
+
+        org.w3c.dom.Document document = XMLUtils.newDocument();
+        SVGGraphics2D        graphics = new SVGGraphics2D(document);
+
+        int[] size = getSize(context);
+
+        chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,size[0],size[1]));
+
+        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,
+        CallContext  cc
+    ) {
+        log.info("export chart as pdf.");
+
+        String pageFormat = (String) cc.getContextValue("chart.page.format");
+
+        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[] size = getSize(cc);
+
+        boolean landscape = size[0] > size[1];
+
+        float width  = 0;
+        float height = 0;
+        if (landscape) {
+            width  = pageHeight;
+            height = pageWidth;
+        }
+        else {
+            width  = pageWidth;
+            height = pageHeight;
+        }
+
+        float marginLeft = (Float) cc.getContextValue(
+            "chart.marginLeft");
+
+        float marginRight = (Float) cc.getContextValue(
+            "chart.marginRight");
+
+        float marginTop = (Float) cc.getContextValue(
+            "chart.marginTop");
+
+        float marginBottom = (Float) cc.getContextValue(
+            "chart.marginBottom");
+
+        float spaceX = width  - marginLeft - marginRight;
+        if (size[0] > spaceX) {
+            log.warn("Width of the chart is too big for pdf -> resize it now.");
+            double ratio = ((double)spaceX) / size[0];
+            size[0]  *= ratio;
+            size[1] *= ratio;
+            log.debug("Resized chart to " + size[0] + "x" + size[1]);
+        }
+
+        float spaceY = height - marginTop  - marginBottom;
+        if (size[1] > spaceY) {
+            log.warn("Height of the chart is too big for pdf -> resize it now.");
+            double ratio = ((double)spaceY) / size[1];
+            size[0]  *= ratio;
+            size[1] *= ratio;
+            log.debug("Resized chart to " + size[0] + "x" + size[1]);
+        }
+
+        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,
+                size[0], size[1]);
+
+            Rectangle2D area = new Rectangle2D.Double(
+                origin[0], origin[1], size[0], size[1]);
+
+            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();
+        }
+    }
+
+
+    public static int[] getSize(CallContext cc) {
+        int[] size = new int[2];
+
+        size[0] = (Integer) cc.getContextValue("chart.width");
+        size[1] = (Integer) cc.getContextValue("chart.height");
+
+        return size;
+    }
+
+
+    /**
+     * 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:51 2012 +0200
@@ -0,0 +1,292 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Locale;
+
+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.CallMeta;
+import de.intevation.artifacts.PreferredLocale;
+
+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.WINFOArtifact;
+
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.utils.FLYSUtils;
+
+
+/**
+ * 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 default chart format, if no other height is set.*/
+    public static final String DEFAULT_CHART_FORMAT = "png";
+
+    /** 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_FORMAT =
+        "/art:action/art:attributes/art:format/@art:value";
+
+    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 Locale getLocale() {
+        CallMeta           meta = context.getMeta();
+        PreferredLocale[] prefs = meta.getLanguages();
+
+        int len = prefs != null ? prefs.length : 0;
+
+        Locale[] locales = new Locale[len];
+
+        for (int i = 0; i < len; i++) {
+            locales[i] = prefs[i].getLocale();
+        }
+
+        return meta.getPreferredLocale(locales);
+    }
+
+
+    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() {
+        WINFOArtifact flys = (WINFOArtifact) master;
+
+        River river = FLYSUtils.getRiver(flys);
+        return (river != null) ? river.getName() : "";
+    }
+
+
+    protected double[] getRange() {
+        WINFOArtifact flys = (WINFOArtifact) master;
+
+        return FLYSUtils.getKmRange(flys);
+    }
+
+
+    /**
+     * 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 String getFormat() {
+        String format = (String) XMLUtils.xpath(
+            request,
+            XPATH_CHART_FORMAT,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return format == null || format.length() == 0
+            ? DEFAULT_CHART_FORMAT
+            : format;
+    }
+
+
+    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,
+        boolean  visible);
+
+    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:51 2012 +0200
@@ -0,0 +1,156 @@
+package de.intevation.flys.exports;
+
+import de.intevation.flys.java2d.NOPGraphics2D;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.awt.Transparency;
+import java.awt.Graphics2D;
+
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+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 {
+
+    public static final boolean USE_NOP_GRAPHICS =
+        Boolean.getBoolean("info.rendering.nop.graphics");
+
+    /** 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,
+        boolean  visible
+    ) {
+        generator.doOut(artifact, facet, attr, visible);
+    }
+
+
+    /**
+     * 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();
+
+        long startTime = System.currentTimeMillis();
+
+        if (USE_NOP_GRAPHICS) {
+            BufferedImage image =
+                new BufferedImage(size[0], size[1], Transparency.BITMASK);
+
+            Graphics2D g2d  = image.createGraphics();
+            Graphics2D nop = new NOPGraphics2D(g2d);
+
+            chart.draw(
+                nop,
+                new Rectangle2D.Double(0, 0, size[0], size[1]),
+                null,
+                info);
+
+            nop.dispose();
+        }
+        else {
+            chart.createBufferedImage(
+                size[0], size[1], Transparency.BITMASK, info);
+        }
+
+        long stopTime = System.currentTimeMillis();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Rendering info took: " +
+                (stopTime-startTime) + "ms");
+        }
+
+
+        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:51 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:51 2012 +0200
@@ -0,0 +1,143 @@
+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 org.jfree.data.xy.XYSeries;
+
+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]";
+    public static final String I18N_MAINVALUES_Q_LABEL = "Q (Haupt- und Extremwerte)";
+    public static final String I18N_MAINVALUES_W_LABEL = "W (Haupt- und Extremwerte)";
+
+
+    /** Trivial Constructor. */
+    public ComputedDischargeCurveGenerator () {
+        super();
+    }
+
+
+    @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,
+        boolean  visible
+    ) {
+        String name = (facet != null) ? facet.getName() : null;
+
+        logger.debug("ComputedDischargeCurveGenerator.doOut: " + name);
+
+        if (name == null) {
+            logger.warn("Broken facet in computed discharge out generation.");
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        Facet        f    = flys.getNativeFacet(facet);
+
+        if (name.equals(COMPUTED_DISCHARGE_Q)) {
+            doQOut((WQKms) f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
+                || name.equals(MAINVALUES_Q)) {
+            doMainValueQAnnotations(
+                f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+                || name.equals(MAINVALUES_W)) {
+            doMainValueWAnnotations(
+                f.getData(artifact, context), facet, attr, visible);
+        }
+        else {
+            logger.warn("Unknown facet type for computed discharge: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Add Q-Series to plot.
+     * @param wqkms actual data
+     * @param theme theme to use.
+     */
+    protected void doQOut(
+        WQKms    wqkms,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        int size = wqkms.size();
+
+        double[]   res  = new double[3];
+
+        XYSeries series = new StyledXYSeries(facet.getDescription(), theme);
+        for (int i = 0; i < size; i++) {
+            res = wqkms.get(i, res);
+            series.add(res[1], res[0]);
+        }
+
+        addFirstAxisSeries(series, visible);
+    }
+}
+// vim: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:51 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/CrossSectionGenerator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,222 @@
+package de.intevation.flys.exports;
+
+import java.awt.Font;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.title.TextTitle;
+import org.jfree.data.xy.XYSeries;
+
+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.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+
+/**
+ * An OutGenerator that generates cross section graphs.
+ */
+public class CrossSectionGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    /** The logger that is used in this generator. */
+    private static Logger logger =
+        Logger.getLogger(CrossSectionGenerator.class);
+
+    public static final String I18N_CHART_TITLE =
+        "chart.cross_section.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.cross_section.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.cross_section.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.cross_section.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT = "Querprofildiagramm";
+    public static final String I18N_XAXIS_LABEL_DEFAULT = "Abstand [m]";
+    public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]";
+
+
+    /** Trivial Constructor. */
+    public CrossSectionGenerator() {
+        super();
+    }
+
+
+    /**
+     * Get localized chart title.
+     */
+    protected String getChartTitle() {
+        // TODO get river etc for localized heading
+        Object[] i18n_msg_args = new Object[] {
+            getRiverName()
+        };
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT, i18n_msg_args);
+    }
+
+
+    /**
+     * Add localized subtitle to chart.
+     */
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        double[] dist = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+            getKm()
+        };
+
+        String subtitle = msg(I18N_CHART_SUBTITLE, "", args);
+        chart.addSubtitle(new TextTitle(subtitle));
+    }
+
+
+    /**
+     * Get localized X Axis label.
+     */
+    protected String getXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    /**
+     * Get cross_section.km data from artifact.
+     */
+    protected Double getKm() {
+        try {
+            WINFOArtifact winfo = (WINFOArtifact) master;
+            return winfo.getCrossSectionSnapKm();
+        }
+        catch (Exception e) {
+            logger.error("Cannot convert cross_section.km to double");
+            return 0.0d;
+        }
+    }
+
+
+    /**
+     * Get localized Y Axis label.
+     */
+    protected String getYAxisLabel() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+
+        NumberAxis qAxis = new NumberAxis(
+            msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT));
+
+        plot.setRangeAxis(1, qAxis);
+
+        Font font = plot.getRangeAxis(0).getLabelFont();
+        qAxis.setLabelFont(font);
+    }
+
+
+    /**
+     * Let one facet do its job.
+     */
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible
+    ) {
+        String name = facet.getName();
+
+        logger.debug("CrossSectionGenerator.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(CROSS_SECTION)) {
+            doCrossSectionOut(
+                f.getData(artifact, context),
+                f.getDescription(),
+                attr,
+                visible);
+        }
+        else if (name.equals(CROSS_SECTION_WATER_LINE)) {
+            doCrossSectionWaterLineOut(
+                f.getData(artifact, context),
+                f.getDescription(),
+                attr,
+                visible);
+        }
+        else {
+            logger.warn("CrossSection.doOut: Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Do cross sections waterline out.
+     *
+     * @param seriesName name of the data (line) to display in legend.
+     * @param theme Theme for the data series.
+     */
+    protected void doCrossSectionWaterLineOut(
+        Object     o,
+        String     seriesName,
+        Document   theme,
+        boolean    visible
+    ) {
+        logger.debug("CrossSectionGenerator.doCrossSectionWaterLineOut");
+
+        // DO NOT SORT DATA! This destroys the gaps indicated by NaNs
+        XYSeries series = new StyledXYSeries(seriesName, false, theme);
+
+        StyledSeriesBuilder.addPoints(series, (double [][]) o);
+
+        addFirstAxisSeries(series, visible);
+    }
+
+
+    /**
+     * Do cross sections out.
+     *
+     * @param seriesName name of the data (line) to display in legend.
+     * @param theme Theme for the data series.
+     */
+    protected void doCrossSectionOut(
+        Object     o,
+        String     seriesName,
+        Document   theme,
+        boolean    visible
+    ) {
+        logger.debug("CrossSectionGenerator.doCrossSectionOut");
+
+        XYSeries series = new StyledXYSeries(seriesName, theme);
+
+        StyledSeriesBuilder.addPoints(series, (double [][]) o);
+
+        addFirstAxisSeries(series, visible);
+    }
+}
+// vim: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/CrossSectionInfoGenerator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,14 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific cross
+ * sections.
+ */
+public class CrossSectionInfoGenerator extends ChartInfoGenerator {
+
+    public CrossSectionInfoGenerator() {
+        super(new CrossSectionGenerator());
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,222 @@
+package de.intevation.flys.exports;
+
+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.XYSeries;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.WQKms;
+
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.jfree.FLYSAnnotation;
+
+/**
+ * An OutGenerator that generates discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeCurveGenerator
+extends      XYChartGenerator
+implements   FacetTypes {
+
+    /** 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]";
+
+
+
+    public DischargeCurveGenerator() {
+        super();
+    }
+
+
+    protected String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    /**
+     * Empty (suppress subtitle).
+     */
+    @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;
+    }
+
+
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("DischargeCurveGenerator.doOut: " + facet.getName());
+
+        String name = facet.getName();
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        Facet nativeFacet = flysArtifact.getNativeFacet(facet);
+
+        if (nativeFacet == null) {
+            logger.debug("no facet found");
+            return;
+        }
+
+        if (name.equals(DISCHARGE_CURVE)) {
+            doDischargeOut(
+                (WINFOArtifact) flysArtifact,
+                nativeFacet.getData(flysArtifact, context),
+                nativeFacet.getDescription(),
+                theme,
+                visible);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
+                || name.equals(MAINVALUES_Q))
+        {
+            doMainValueQAnnotations(
+                nativeFacet.getData(artifact, context), facet, theme, visible);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
+                || name.equals(MAINVALUES_W))
+        {
+            doMainValueWAnnotations(
+                nativeFacet.getData(artifact, context), facet, theme, visible);
+        }
+        else {
+           logger.warn("DischargeCurveGenerator.doOut: Unknown facet name: " + name);
+           return;
+        }
+    }
+
+
+    /**
+     * Add series with discharge curve to diagram.
+     */
+    protected void doDischargeOut(
+        WINFOArtifact artifact,
+        Object o,
+        String description,
+        Document theme,
+        boolean visible)
+    {
+        WQKms wqkms = (WQKms) o;
+
+        String gaugeName = wqkms.getName();
+
+        River river = FLYSUtils.getRiver(artifact);
+
+        if (river == null) {
+            logger.debug("no river found");
+            return;
+        }
+
+        Gauge gauge = river.determineGaugeByName(gaugeName);
+
+        if (gauge == null) {
+            logger.debug("no gauge found");
+            return;
+        }
+
+        XYSeries series = new StyledXYSeries(description, theme);
+
+        StyledSeriesBuilder.addPointsQW(series, wqkms);
+
+        addFirstAxisSeries(series, visible);
+    }
+
+            
+
+    /**
+     * Store W MainValues as annotations for later plotting.
+     */
+    protected void doMainValueWAnnotations(
+        Object   o,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("ComputedDischargeCurveGenerator set W MainValues.");
+
+        FLYSAnnotation fa = (FLYSAnnotation) o;
+        fa.setTheme(theme);
+        fa.setLabel(facet.getDescription());
+        addAnnotations(fa, visible);
+    }
+
+
+    /**
+     * Store Q MainValues as annotations for later plotting.
+     */
+    protected void doMainValueQAnnotations(
+        Object   o,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("ComputedDischargeCurveGenerator set Q MainValues.");
+
+        FLYSAnnotation fa = (FLYSAnnotation) o;
+        fa.setTheme(theme);
+        fa.setLabel(facet.getDescription());
+        addAnnotations(fa, visible);
+    }
+
+
+
+}
+// vim: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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,110 @@
+package de.intevation.flys.exports;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.data.xy.XYSeries;
+
+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);
+
+
+
+    public DischargeLongitudinalSectionGenerator() {
+        super();
+    }
+
+
+    @Override
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible
+    ) {
+        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), facet, attr, visible);
+        }
+        else if (name.equals(DISCHARGE_LONGITUDINAL_Q)) {
+            doQOut((WQKms) f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(DISCHARGE_LONGITUDINAL_C)) {
+            doCorrectedWOut(
+                (WQCKms) f.getData(artifact, context),
+                facet,
+                attr,
+                visible);
+        }
+        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.
+     * @param theme The theme that contains styling information.
+     */
+    protected void doCorrectedWOut(
+        WQCKms   wqckms,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("DischargeLongitudinalSectionGenerator.doCorrectedWOut");
+
+        int size = wqckms.size();
+
+        if (size > 0) {
+            XYSeries series = new StyledXYSeries(
+                facet.getDescription(),
+                theme);
+
+            for (int i = 0; i < size; i++) {
+                series.add(wqckms.getKm(i), wqckms.getC(i));
+            }
+
+            addFirstAxisSeries(series, visible);
+        }
+
+        if (wqckms.guessWaterIncreasing()) {
+            setInverted(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/exports/DischargeLongitudinalSectionInfoGenerator.java	Fri Sep 28 12:14:51 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:51 2012 +0200
@@ -0,0 +1,149 @@
+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();
+
+        if (wqday.isIncreasing()) {
+            for (int i = size-1; i >= 0; i --) {
+                writer.writeNext(new String[] {
+                    wf.format(wqday.getW(i)),
+                    qf.format(wqday.getQ(i)),
+                    df.format(wqday.getDay(i))
+                });
+            }
+        }
+        else {
+            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:51 2012 +0200
@@ -0,0 +1,231 @@
+package de.intevation.flys.exports;
+
+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.title.TextTitle;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYSeries;
+
+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);
+
+
+    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();
+    }
+
+
+    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);
+        }
+
+        axis.setUpperBound(364);
+
+        return zoomin;
+    }
+
+
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+
+        NumberAxis qAxis = new NumberAxis("Q [m\u00b3/s]");
+
+        plot.setRangeAxis(1, qAxis);
+    }
+
+
+    @Override
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible
+    ) {
+        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), attr, visible);
+        }
+        else if (name.equals(DURATION_Q)) {
+            doQOut((WQDay) f.getData(artifact, context), attr, visible);
+        }
+        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 theme
+     */
+    protected void doWOut(WQDay wqdays, Document theme, boolean visible) {
+        logger.debug("DurationCurveGenerator.doWOut");
+
+        // TODO find the correct series name
+        XYSeries series = new StyledXYSeries(
+            getSeriesName(getRiverName(), DURATION_W), theme);
+
+        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);
+        }
+
+        addFirstAxisSeries(series, visible);
+    }
+
+
+    /**
+     * Creates the series for a duration curve's Q facet.
+     *
+     * @param wqdays The WQDay store that contains the Qs.
+     * @param theme
+     */
+    protected void doQOut(WQDay wqdays, Document theme, boolean visible) {
+        logger.debug("DurationCurveGenerator.doQOut");
+
+        // TODO find the correct series name
+        XYSeries series = new StyledXYSeries(
+            getSeriesName(getRiverName(), DURATION_Q), theme);
+
+        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);
+        }
+
+        addSecondAxisSeries(series, visible);
+    }
+
+
+    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:51 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:51 2012 +0200
@@ -0,0 +1,316 @@
+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[] rs = generator.getRangesForDataset(pos);
+        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:51 2012 +0200
@@ -0,0 +1,467 @@
+package de.intevation.flys.exports;
+
+import java.awt.Font;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.title.TextTitle;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYSeries;
+
+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.WKms;
+import de.intevation.flys.artifacts.model.WQKms;
+
+import de.intevation.flys.jfree.FLYSAnnotation;
+
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.utils.DataUtil;
+
+
+/**
+ * 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);
+
+    /** Key to look up internationalized String for annotations label. */
+    public static final String I18N_ANNOTATIONS_LABEL =
+        "chart.longitudinal.annotations.label";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * titles.
+     */
+    public static final String I18N_CHART_TITLE =
+        "chart.longitudinal.section.title";
+
+    /**
+     * Key to look up internationalized String for LongitudinalSection diagrams
+     * subtitles.
+     */
+    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]";
+
+    /** Whether or not the plot is inverted (left-right). */
+    protected boolean inverted;
+
+
+    public LongitudinalSectionGenerator() {
+        super();
+    }
+
+
+    public boolean isInverted() {
+        return inverted;
+    }
+
+
+    public void setInverted(boolean inverted) {
+        this.inverted = inverted;
+    }
+
+
+    /**
+     * Get internationalized title for chart.
+     */
+    public String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    /**
+     * Gets key to look up internationalized String for the charts subtitle.
+     * @return key to look up translated subtitle.
+     */
+    protected String getChartSubtitleKey() {
+        return I18N_CHART_SUBTITLE;
+    }
+
+
+    /**
+     * Add (internationalized) subtitle to chart.
+     * @see getChartSubtitleKey
+     */
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        double[] dist = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+
+            dist[0],
+
+            dist[1]
+        };
+
+        String subtitle = msg(getChartSubtitleKey(), "", args);
+        chart.addSubtitle(new TextTitle(subtitle));
+    }
+
+
+    /**
+     * Get internationalized label for the x axis.
+     */
+    protected String getXAxisLabel() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        return msg(
+            I18N_XAXIS_LABEL,
+            I18N_XAXIS_LABEL_DEFAULT,
+            new Object[] { FLYSUtils.getRiver(flys).getName() });
+    }
+
+
+    /**
+     * Get internationalized label for the y axis.
+     */
+    protected String getYAxisLabel() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
+
+        return msg(
+            I18N_YAXIS_LABEL,
+            I18N_YAXIS_LABEL_DEFAULT,
+            new Object[] { unit });
+    }
+
+
+    /**
+     * Get default value for the second Y-Axis' label (if no translation was
+     * found).
+     */
+    protected String get2YAxisDefaultLabel() {
+        return I18N_2YAXIS_LABEL_DEFAULT;
+    }
+
+
+    /**
+     * Get key for internationalization of the second Y-Axis' label.
+     */
+    protected String get2YAxisLabelKey() {
+        return I18N_2YAXIS_LABEL;
+    }
+
+
+    /**
+     * Adjust the axis to meet LongitudinalSection diagram demands.
+     * (e.g. add second Y-axis with internationalized label, trigger
+     * inversion).
+     * @param see get2YAxisLabelKey, get2YAxisDefaultLabel
+     */
+    @Override
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+
+        NumberAxis qAxis = new NumberAxis(
+            msg(get2YAxisLabelKey(), get2YAxisDefaultLabel()));
+
+        plot.setRangeAxis(1, qAxis);
+
+        Font font = plot.getRangeAxis(0).getLabelFont();
+        qAxis.setLabelFont(font);
+
+        invertXAxis(plot.getDomainAxis());
+    }
+
+
+    /**
+     * Create a range that includes 0 (for the Q axis).
+     * @param range range with which to look up upper bound.
+     * @return range with 0 included.
+     */
+    protected Range createSecondAxisRange(Range range) {
+        if (range == null) {
+            return null;
+        }
+        return new Range(0d, range.getUpperBound());
+    }
+
+
+    /**
+     * This method overrides the XYChartGenerators zoomY method to be able to
+     * modify the range of the Q axis (here, it shall include 0).
+     */
+    @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 = createSecondAxisRange(range);
+        }
+
+        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("X-Axis.setInverted(true)");
+            xaxis.setInverted(true);
+        }
+    }
+
+
+    /**
+     * Produce output.
+     * @param facet current facet.
+     * @param attr  theme for facet
+     */
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible
+    ) {
+        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), facet, attr, visible);
+        }
+        else if (name.equals(LONGITUDINAL_Q)) {
+            doQOut((WQKms) f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotationsOut(f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(STATIC_WKMS) || name.equals(HEIGHTMARKS_POINTS)) {
+            doWOut((WKms) f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(W_DIFFERENCES)) {
+            doWDifferencesOut(
+                (WKms) f.getData(artifact, context),
+                facet,
+                attr,
+                visible);
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Register annotations available for the diagram.
+     *
+     * @param o     list of annotations (data of facet).
+     * @param facet The facet. This facet does NOT support any data objects. Use
+     * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports
+     * data.
+     * @param theme yet ignored.
+     * @param visible The visibility of the annotations.
+     */
+    protected void doAnnotationsOut(
+        Object   o,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("LongitudinalSectionGenerator.doAnnotationsOut");
+
+        // Add all annotations in list o to our annotation pool.
+        // TODO Duplicate in ComputedDischartgeCurveGenerator, merge
+        FLYSAnnotation fa = (FLYSAnnotation) o;
+        fa.setTheme(theme);
+        fa.setLabel(facet.getDescription());
+        addAnnotations(fa, visible);
+    }
+
+
+    /**
+     * Process the output for W facets in a longitudinal section curve.
+     *
+     * @param wqkms An array of WQKms values.
+     * @param facet The facet. This facet does NOT support any data objects. Use
+     * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports
+     * data.
+     * @param theme The theme that contains styling information.
+     * @param visible The visibility of the curve.
+     */
+    protected void doWOut(
+        WKms     wkms,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("LongitudinalSectionGenerator.doWOut");
+
+        XYSeries series = new StyledXYSeries(facet.getDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, wkms);
+
+        addFirstAxisSeries(series, visible);
+
+        if (wkms instanceof WQKms) {
+            if (needInvertAxis((WQKms) wkms)) {
+                setInverted(true);
+            }
+        }
+    }
+
+
+    /**
+     * Add items to dataseries which describes the differences.
+     */
+    protected void doWDifferencesOut(
+        WKms       wkms,
+        Facet      facet,
+        Document   theme,
+        boolean    visible
+    ) {
+        logger.debug("WDifferencesCurveGenerator.doWDifferencesOut");
+        if (wkms == null) {
+            logger.warn("No data to add to WDifferencesChart.");
+            return;
+         }
+
+        XYSeries series = new StyledXYSeries(facet.getDescription(), theme);
+
+        if (logger.isDebugEnabled()) {
+            if (wkms.size() > 0) {
+                logger.debug("Generate series: " + series.getKey());
+                logger.debug("Start km: " + wkms.getKm(0));
+                logger.debug("End   km: " + wkms.getKm(wkms.size() - 1));
+                logger.debug("Values  : " + wkms.size());
+            }
+        }
+
+        StyledSeriesBuilder.addPoints(series, wkms);
+
+        addSecondAxisSeries(series, visible);
+        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
+            setInverted(true);
+        }
+    }
+
+
+
+    /**
+     * Process the output for Q facets in a longitudinal section curve.
+     *
+     * @param wqkms An array of WQKms values.
+     * @param facet The facet. This facet does NOT support any data objects. Use
+     * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports
+     * data.
+     * @param theme The theme that contains styling information.
+     * @param visible The visibility of the curve.
+     */
+    protected void doQOut(
+        WQKms    wqkms,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("LongitudinalSectionGenerator.doQOut");
+
+        XYSeries series = new StyledXYSeries(facet.getDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, wqkms);
+
+        addSecondAxisSeries(series, visible);
+
+        if (needInvertAxis(wqkms)) {
+            setInverted(true);
+        }
+    }
+
+
+    /**
+     * This method determines - taking JFreeCharts auto x value ordering into
+     * account - if the x axis need to be inverted. Waterlines in these charts
+     * should decrease.
+     *
+     * @param wqkms The data object that stores the x and y values used for this
+     * chart.
+     */
+    public boolean needInvertAxis(WQKms wqkms) {
+        boolean wsUp = wqkms.guessWaterIncreasing();
+        boolean kmUp = DataUtil.guessWaterIncreasing(wqkms.allKms());
+        boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp);
+
+        int size = wqkms.size();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Values  : " + size);
+            if (size > 0) {
+                logger.debug("Start km: " + wqkms.getKm(0));
+                logger.debug("End   km: " + wqkms.getKm(size-1));
+            }
+            logger.debug("wsUp: " + wsUp);
+            logger.debug("kmUp: " + kmUp);
+            logger.debug("inv:  " + inv);
+        }
+
+        return inv;
+    }
+
+
+    /**
+     * Get name of series (displayed in legend).
+     * @return name of the series.
+     */
+    protected String getSeriesName(WQKms wqkms, String mode) {
+        String name   = wqkms.getName();
+        String prefix = name != null && name.indexOf(mode) >= 0 ? null : mode;
+
+        return prefix != null && prefix.length() > 0
+            ? prefix + "(" + name +")"
+            : name;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java	Fri Sep 28 12:14:51 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/MapGenerator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,287 @@
+package de.intevation.flys.exports;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+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.WMSDBLayerFacet;
+import de.intevation.flys.artifacts.model.WMSLayerFacet;
+import de.intevation.flys.utils.GeometryUtils;
+import de.intevation.flys.utils.MapfileGenerator;
+import de.intevation.flys.utils.ThemeUtil;
+
+
+public class MapGenerator implements OutGenerator, FacetTypes {
+
+    private static Logger logger = Logger.getLogger(MapGenerator.class);
+
+
+    protected Artifact master;
+
+    protected Document request;
+
+    protected OutputStream out;
+
+    protected CallContext context;
+
+    protected List<WMSLayerFacet> layers;
+
+    protected Envelope maxExtent;
+    protected Envelope initialExtent;
+
+    protected String srid;
+
+
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("MapGenerator.init");
+
+        this.request  = request;
+        this.out      = out;
+        this.context  = context;
+
+        this.layers = new ArrayList<WMSLayerFacet>();
+
+        this.maxExtent = null;
+        this.initialExtent = null;
+    }
+
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        logger.debug("MapGenerator.setMasterArtifact");
+        this.master = master;
+    }
+
+
+    @Override
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible)
+    {
+        String name = facet.getName();
+
+        logger.debug("MapGenerator.doOut: " +artifact.identifier()+" | "+name);
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        Facet nativeFacet = flys.getNativeFacet(facet);
+
+        if (nativeFacet instanceof WMSLayerFacet) {
+            WMSLayerFacet wms = (WMSLayerFacet) nativeFacet;
+            Envelope   extent = wms.getExtent();
+
+            layers.add(wms);
+
+            setMaxExtent(extent);
+            setSrid(wms.getSrid());
+
+            if (FLOODMAP_WSPLGEN.equals(name)) {
+                if (initialExtent == null) {
+                    setInitialExtent(extent);
+                }
+
+                createWSPLGENLayer(flys, wms);
+            }
+            else if (FLOODMAP_BARRIERS.equals(name)) {
+                createBarriersLayer(flys, wms);
+            }
+            else {
+                createDatabaseLayer(flys, wms, attr);
+            }
+        }
+        else {
+            logger.warn("Facet not supported: " + nativeFacet.getClass());
+        }
+    }
+
+
+    protected void createWSPLGENLayer(FLYSArtifact flys, WMSLayerFacet wms) {
+        try {
+            MapfileGenerator mfg = MapfileGenerator.getInstance();
+            mfg.createUeskLayer(flys, wms);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    protected void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms) {
+        MapfileGenerator mfg = MapfileGenerator.getInstance();
+
+        try {
+            mfg.createBarriersLayer(flys, wms);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    protected void createDatabaseLayer(
+        FLYSArtifact  flys,
+        WMSLayerFacet wms,
+        Document      attr
+    ) {
+        logger.debug("createDatabaseLayer for facet: " + wms.getName());
+
+        MapfileGenerator mfg = MapfileGenerator.getInstance();
+
+        try {
+            File baseDir = mfg.getShapefileBaseDir();
+            File artDir  = new File(baseDir, flys.identifier());
+
+            if (artDir != null && !artDir.exists()) {
+                logger.debug("Create new directory: " + artDir.getPath());
+                artDir.mkdir();
+            }
+
+            if (wms instanceof WMSDBLayerFacet) {
+                mfg.createDatabaseLayer(
+                    flys,
+                    (WMSDBLayerFacet) wms,
+                    ThemeUtil.createMapserverStyle(attr));
+            }
+            else {
+                logger.warn("Cannot create DB layer from: " + wms.getClass());
+            }
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+    }
+
+
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("MapGenerator.generate");
+
+        MapfileGenerator.getInstance().update();
+
+        Document response = XMLUtils.newDocument();
+        ElementCreator c  = new ElementCreator(
+            response,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root   = c.create("floodmap");
+        Element layers = c.create("layers");
+
+        response.appendChild(root);
+        root.appendChild(layers);
+
+        appendLayers(layers);
+        appendMapInformation(root, c);
+
+        XMLUtils.toStream(response, out);
+    }
+
+
+    protected void appendLayers(Element parent) {
+        for (WMSLayerFacet facet: layers) {
+            parent.appendChild(facet.toXML(parent.getOwnerDocument()));
+        }
+    }
+
+
+    protected void setMaxExtent(Envelope maxExtent) {
+        if (maxExtent == null) {
+            return;
+        }
+
+        if (this.maxExtent == null) {
+            logger.debug("Set max extent to: " + maxExtent);
+            this.maxExtent = maxExtent;
+            return;
+        }
+
+        logger.debug("Expand max extent by: " + maxExtent);
+        logger.debug("Max extent before expanding: " + this.maxExtent);
+        this.maxExtent.expandToInclude(maxExtent);
+        logger.debug("Max extent after expanding: " + this.maxExtent);
+    }
+
+
+    protected void setInitialExtent(Envelope initialExtent) {
+        if (initialExtent == null) {
+            return;
+        }
+
+        if (this.initialExtent == null) {
+            logger.debug("Set initial extent to: " + initialExtent);
+            this.initialExtent = initialExtent;
+            return;
+        }
+
+        logger.debug("Set initial extent to: " + initialExtent);
+        this.initialExtent = initialExtent;
+    }
+
+
+    protected void setSrid(String srid) {
+        if (srid == null || srid.length() == 0) {
+            return;
+        }
+
+        this.srid = srid;
+    }
+
+
+    protected void appendMapInformation(Element parent, ElementCreator c) {
+        String mE = GeometryUtils.jtsBoundsToOLBounds(this.maxExtent);
+        logger.debug("BUILD MAX EXTENT OF:" + this.maxExtent);
+        logger.debug("BUILD MAX EXTENT:" + mE);
+
+        Element maxExtent = c.create("maxExtent");
+        maxExtent.setTextContent(mE);
+
+        String iE = GeometryUtils.jtsBoundsToOLBounds(this.initialExtent);
+        logger.debug("BUILD INITIAL EXTENT OF: " + this.initialExtent);
+        logger.debug("BUILD INITIAL EXTENT: " + iE);
+        Element initExtent = c.create("initialExtent");
+        initExtent.setTextContent(iE);
+
+        Element srid = c.create("srid");
+        srid.setTextContent(this.srid);
+
+        // TODO zoom levels
+        // TODO resolutation
+
+        parent.appendChild(maxExtent);
+        parent.appendChild(initExtent);
+        parent.appendChild(srid);
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,59 @@
+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.
+     * @param visible Specifies, if this output should be visible or not.
+     */
+    void doOut(Artifact artifact, Facet facet, Document attr, boolean visible);
+
+    /**
+     * 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:51 2012 +0200
@@ -0,0 +1,67 @@
+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,
+        boolean  visible
+    ) {
+        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/StyledSeriesBuilder.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,84 @@
+package de.intevation.flys.exports;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.data.xy.XYSeries;
+
+import de.intevation.flys.artifacts.model.WKms; 
+import de.intevation.flys.artifacts.model.WQKms; 
+
+/**
+ * Helper to create and modify StyledXYSeries.
+ */
+public class StyledSeriesBuilder {
+
+    private static final Logger logger = Logger.getLogger(StyledSeriesBuilder.class);
+
+
+    /**
+     * Trivial, hidden constructor.
+     */
+    private StyledSeriesBuilder() {;}
+
+
+    /**
+     * Add points to series.
+     *
+     * @param series Series to add points to.
+     * @param points Points to add to series, points[0] to 1st dim, points[1]
+     *               to 2nd dim.
+     */
+    public static void addPoints(XYSeries series, double[][] points) {
+        double [] xPoints = points[0];
+        double [] yPoints = points[1];
+        for (int i = 0; i < xPoints.length; i++) {
+            series.add(xPoints[i], yPoints[i], false);
+        }
+    }
+
+
+    /**
+     * Add points to series (km to 1st dim, w to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param points Points to add to series.
+     */
+    public static void addPoints(XYSeries series, WKms wkms) {
+        int size = wkms.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wkms.getKm(i), wkms.getW(i), false);
+        }
+    }
+
+
+    /**
+     * Add points to series (w to 1st dim, km to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param points Points to add to series.
+     */
+    public static void addPointsKmQ(XYSeries series, WQKms wqkms) {
+       int size = wqkms.size();
+
+       for (int i = 0; i < size; i++) {
+           series.add(wqkms.getKm(i), wqkms.getQ(i), false);
+       }
+    }
+
+
+    /**
+     * Add points to series (q to 1st dim, w to 2nd dim).
+     *
+     * @param series Series to add points to.
+     * @param points Points to add to series.
+     */
+    public static void addPointsQW(XYSeries series, WQKms wqkms) {
+        int size = wqkms.size();
+
+        for (int i = 0; i < size; i++) {
+            series.add(wqkms.getQ(i), wqkms.getW(i));
+        }
+    }
+}
+// vim: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/StyledXYSeries.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,101 @@
+package de.intevation.flys.exports;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.geom.Ellipse2D;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+
+import de.intevation.flys.utils.ThemeUtil;
+
+/**
+ * Dataset in two dimensions with additional theme-document.
+ * The theme-document will later "style" the graphical representation.
+ */
+public class StyledXYSeries extends XYSeries {
+
+    protected Document theme;
+
+    private static final Logger logger = Logger.getLogger(StyledXYSeries.class);
+
+
+    public StyledXYSeries(String key, Document theme) {
+        this(key, true, theme);
+    }
+
+
+    /**
+     * @param sorted whether or not to sort the points. Sorting will move NANs
+     *               to one extrema which can cause problems in certain
+     *               algorithms.
+     */
+    public StyledXYSeries(String key, boolean sorted, Document theme) {
+        super(key, sorted);
+        this.theme = theme;
+    }
+
+
+    public XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx){
+        applyLineColor(r, idx);
+        applyLineSize(r, idx);
+        applyLineType(r, idx);
+        applyShowLine(r, idx);
+        applyShowPoints(r, idx);
+
+        return r;
+    }
+
+
+    protected void applyLineColor(XYLineAndShapeRenderer r, int idx) {
+        Color c = ThemeUtil.parseLineColorField(theme);
+        r.setSeriesPaint(idx, c);
+    }
+
+
+    protected void applyLineSize(XYLineAndShapeRenderer r, int idx) {
+        int size = ThemeUtil.parseLineWidth(theme);
+        r.setSeriesStroke(
+            idx,
+            new BasicStroke(Integer.valueOf(size)));
+    }
+
+
+    protected void applyLineType(XYLineAndShapeRenderer r, int idx) {
+        int size = ThemeUtil.parseLineWidth(theme);
+        float[] dashes = ThemeUtil.parseLineStyle(theme);
+
+        // Do not apply the dashed style.
+        if (dashes.length <= 1) {
+            return;
+        }
+
+        r.setSeriesStroke(
+            idx,
+            new BasicStroke(Integer.valueOf(size),
+                            BasicStroke.CAP_BUTT,
+                            BasicStroke.JOIN_ROUND,
+                            1.0f,
+                            dashes,
+                            0.0f));
+    }
+
+
+    protected void applyShowPoints(XYLineAndShapeRenderer r, int idx) {
+        boolean show = ThemeUtil.parseShowPoints(theme);
+        r.setSeriesShape(idx, new Ellipse2D.Double(-2.0, -2.0, 4.0, 4.0));
+        r.setSeriesShapesVisible(idx, show);
+        r.setDrawOutlines(true);
+    }
+
+
+    protected void applyShowLine(XYLineAndShapeRenderer r, int idx) {
+        boolean show = ThemeUtil.parseShowLine(theme);
+        r.setSeriesLinesVisible(idx, 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-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,295 @@
+package de.intevation.flys.exports;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.title.TextTitle;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.Range;
+
+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.WKms;
+
+import de.intevation.flys.utils.DataUtil;
+
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.utils.FLYSUtils;
+
+
+/**
+ * An OutGenerator that generates w differences curves.
+ */
+public class WDifferencesCurveGenerator
+extends      LongitudinalSectionGenerator
+implements   FacetTypes
+{
+    /** The logger that is used in this generator. */
+    private static Logger logger =
+        Logger.getLogger(WDifferencesCurveGenerator.class);
+
+    /** Key for internationalized title of WDiff charts. */
+    public final static String I18N_WDIFF_TITLE = "chart.w_differences.title";
+
+    /** Default for internationalized title (when no translation found). */
+    public final static String I18N_WDIFF_TITLE_DEFAULT = "Differences";
+
+    /** Key for internationalized title of WDiff charts. */
+    public final static String I18N_WDIFF_2YAXIS_LABEL =
+        "chart.w_differences.yaxis.second.label";
+
+    /** Default for label for second Y-Axis when no translation found. */
+    public final static String I18N_WDIFF_2YAXIS_LABEL_DEFAULT = "W [NN + m]";
+
+    public final static String I18N_WDIFF_SUBTITLE =
+        "chart.w_differences.subtitle";
+
+    public final static String I18N_WDIFF_YAXIS_LABEL =
+        "chart.w_differences.yaxis.label";
+
+    public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m";
+
+
+    /**
+     * Get internationalized title for chart.
+     * @return internationalized Chart title.
+     */
+    @Override
+    public String getChartTitle() {
+        return msg(I18N_WDIFF_TITLE, I18N_WDIFF_TITLE_DEFAULT);
+    }
+
+
+    /**
+     * Get default value for the second Y-Axis' label (if no translation was
+     * found).
+     * @return default value for second y-axis label.
+     */
+    @Override
+    protected String get2YAxisDefaultLabel() {
+        return I18N_WDIFF_2YAXIS_LABEL_DEFAULT;
+    }
+
+
+    /**
+     * Gets key to look up internationalized String for the charts subtitle.
+     * @return key to look up translated subtitle.
+     */
+    @Override
+    protected String getChartSubtitleKey() {
+        return I18N_WDIFF_SUBTITLE;
+    }
+
+
+    /**
+     * Get key for internationalization of the second Y-Axis' label.
+     * @return internationalized second y-axis.
+     */
+    @Override
+    protected String get2YAxisLabelKey() {
+        return I18N_WDIFF_2YAXIS_LABEL;
+    }
+
+
+    /**
+     * Get internationalized label for the y axis.
+     * @return internationalized label for y axos.
+     */
+    @Override
+    protected String getYAxisLabel() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        String unit = FLYSUtils.getRiver(flys).getWstUnit().getName();
+
+        return msg(
+            I18N_WDIFF_YAXIS_LABEL,
+            I18N_WDIFF_YAXIS_LABEL_DEFAULT,
+            new Object[] { unit });
+    }
+
+
+    /**
+     * Add (themed) data for chart generation.
+     */
+    @Override
+    public void doOut(
+        Artifact artifact,
+        Facet    facet,
+        Document attr,
+        boolean  visible
+    ) {
+        String name = facet.getName();
+
+        logger.debug("WDifferencesCurveGenerator.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(W_DIFFERENCES)) {
+            doWDifferencesOut(
+                (WKms) f.getData(artifact, context),
+                facet,
+                attr,
+                visible);
+        }
+        else if (name.equals(LONGITUDINAL_W)) {
+            doWOut((WQKms) f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(STATIC_WKMS)) {
+            doWOut((WKms) f.getData(artifact, context), facet, attr, visible);
+        }
+        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
+            doAnnotationsOut(f.getData(artifact, context), facet, attr, visible);
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Add the waterlevel-curves (the "absolutes" from which
+     * differences were calculated).
+     *
+     * @param wqkms The wqkms to add to the diagram.
+     * @param theme The theme that contains styling information.
+     */
+    protected void doWOut(
+        WKms     wkms,
+        Facet    facet,
+        Document theme,
+        boolean  visible
+    ) {
+        logger.debug("WDifferencesCurveGenerator.doWOut");
+
+        XYSeries series = new StyledXYSeries(facet.getDescription(), theme);
+
+        StyledSeriesBuilder.addPoints(series, wkms);
+
+        // Note: the only difference in the super-implementation
+        //  (in LongitudinalSectionGenerator) is here (adds with
+        //  addFirstAxisSeries() .
+        addSecondAxisSeries(series, visible);
+
+        if (wkms instanceof WQKms) {
+            if (needInvertAxis((WQKms) wkms)) {
+                setInverted(true);
+            }
+        }
+    }
+
+    /**
+     * Add (internationalized) subtitle to chart.
+     * Overridden to avoid trying to access the range of masterartifact.
+     * @see getChartSubtitleKey
+     */
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+
+        // TODO i18n
+        /*
+        Object[] args = new Object[] {
+            getRiverName()
+        };
+        */
+        String subtitle = getRiverName();
+        chart.addSubtitle(new TextTitle(subtitle));
+    }
+
+
+    /**
+     * Add items to dataseries which describes the differences.
+     */
+    protected void doWDifferencesOut(
+        WKms       wkms,
+        Facet      facet,
+        Document   theme,
+        boolean    visible
+    ) {
+        logger.debug("WDifferencesCurveGenerator.doWDifferencesOut");
+        if (wkms == null) {
+            logger.warn("No data to add to WDifferencesChart.");
+            return;
+         }
+
+        XYSeries series = new StyledXYSeries(facet.getDescription(), theme);
+
+        if (logger.isDebugEnabled()) {
+            if (wkms.size() > 0) {
+                logger.debug("Generate series: " + series.getKey());
+                logger.debug("Start km: " + wkms.getKm(0));
+                logger.debug("End   km: " + wkms.getKm(wkms.size()-1));
+                logger.debug("Values  : " + wkms.size());
+            }
+        }
+
+        StyledSeriesBuilder.addPoints(series, wkms);
+
+        addFirstAxisSeries(series, visible);
+        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
+            setInverted(true);
+        }
+    }
+
+    /**
+     * Disable Longitudinals behaviour to include "0" in the Q axis.
+     *
+     * @param range range with which to look up upper bound.
+     * @return range to be used for "auto-scaling" axis.
+     */
+    @Override
+    protected Range createSecondAxisRange(Range range) {
+        if (range == null) {
+            return range;
+        }
+        return new Range(range.getLowerBound(), range.getUpperBound());
+    }
+
+ 
+    /**
+     * 
+     */
+    @Override
+    public JFreeChart generateChart() {
+        JFreeChart chart = super.generateChart();
+        if (chart != null && chart.getPlot() != null) {
+            XYPlot plot = (XYPlot) chart.getPlot();
+            plot.setRangeZeroBaselineVisible(true);
+        }
+        return chart;
+    }
+
+    /**
+     * Get name of series (displayed in legend).
+     * @return name of the series.
+     */
+    protected String getSeriesName(WKms 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/WDifferencesCurveInfoGenerator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,15 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific
+ * w differences.
+ */
+public class WDifferencesCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public WDifferencesCurveInfoGenerator() {
+        super(new WDifferencesCurveGenerator());
+    }
+}
+// vim: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/WDifferencesExporter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,161 @@
+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.WKms;
+
+import de.intevation.flys.utils.Formatter;
+
+/**
+ * (CSV)Exporter for WDifferences.
+ */
+public class WDifferencesExporter extends AbstractExporter {
+
+    /** The logger used in this exporter. */
+    private static Logger logger = Logger.getLogger(WDifferencesExporter.class);
+
+
+    public static final String WDIFF_CSV_KM_HEADER =
+        "export.w_differences.csv.header.km";
+
+    public static final String WDIFF_CSV_W_HEADER =
+        "export.w_differences.csv.header.w";
+
+    public static final String WDIFF_DEFAULT_CSV_KM_HEADER = "Fluss-Km";
+    public static final String WDIFF_DEFAULT_CSV_W_HEADER  = "m";
+
+
+    /** The storage that contains all WKms objects for the different facets. */
+    protected List<WKms[]> data;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("WDifferencesExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WKms[]>();
+    }
+
+
+    /**
+     * Genereate data in csv format.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("WDifferencesExporter.generate");
+
+        if (facet == null) {
+            throw new IOException("invalid (null) facet for exporter");
+        }
+        else if (facet.equals(AbstractExporter.FACET_CSV)) {
+            generateCSV();
+        }
+        else {
+            throw new IOException("invalid facet (" + facet + ") for exporter");
+        }
+    }
+
+
+    /**
+     * Adds given data.
+     * @param d either a WKms or a CalculationResult to add to data.
+     */
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WKms []) {
+                data.add((WKms [])d);
+            }
+        }
+        else if (d instanceof WKms) {
+            data.add(new WKms[] { (WKms) d });
+        }
+    }
+
+
+    /**
+     * Lets writer write all data (including header).
+     * @param writer Writer to write data with.
+     */
+    @Override
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("WDifferencesExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        for (WKms[] tmp: data) {
+            for (WKms wkms: tmp) {
+                wKms2CSV(writer, wkms);
+            }
+        }
+    }
+
+
+    /**
+     * Lets csvwriter write the header (first line in file).
+     * @param write Writer to write header with.
+     */
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("WDifferencesExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(WDIFF_CSV_KM_HEADER, WDIFF_DEFAULT_CSV_KM_HEADER),
+            msg(WDIFF_CSV_W_HEADER, WDIFF_DEFAULT_CSV_W_HEADER)
+        });
+    }
+
+
+    protected void wKms2CSV(CSVWriter writer, WKms wkms) {
+        logger.debug("WDifferencesExporter.wQKms2CSV");
+
+        NumberFormat kmf  = getKmFormatter();
+        NumberFormat wf   = getWFormatter();
+        int          size = wkms.size();
+
+        for (int i = 0; i < size; i ++) {
+
+            writer.writeNext(new String[] {
+                kmf.format(wkms.getKm(i)),
+                wf.format(wkms.getW(i))
+            });
+        }
+    }
+
+
+    /**
+     * 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);
+    }
+}
+// vim: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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,646 @@
+package de.intevation.flys.exports;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Stroke;
+
+import java.io.IOException;
+
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYTextAnnotation;
+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.chart.renderer.xy.XYItemRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.jfree.ui.RectangleInsets;
+
+import de.intevation.flys.exports.ChartExportHelper;
+import de.intevation.flys.jfree.FLYSAnnotation;
+import de.intevation.flys.jfree.StickyAxisAnnotation;
+
+import de.intevation.flys.utils.ThemeAccess;
+
+/**
+ * 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);
+
+    /** SeriesCollection used for the first axis. */
+    protected XYSeriesCollection first;
+
+    /** SeriesCollection used for the second axis. */
+    protected XYSeriesCollection second;
+
+    /** List of annotations to insert in plot. */
+    protected List<FLYSAnnotation> annotations;
+
+    /** The max X range to include all X values of all series for each axis. */
+    protected Map<Integer, Range> xRanges;
+
+    /** The max Y range to include all Y values of all series for each axis. */
+    protected Map<Integer, Range> yRanges;
+
+    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();
+
+
+    public void generate()
+    throws IOException
+    {
+        logger.debug("XYChartGenerator.generate");
+
+        JFreeChart chart = generateChart();
+
+        String format = getFormat();
+        int[]  size   = getSize();
+
+        context.putContextValue("chart.width", size[0]);
+        context.putContextValue("chart.height", size[1]);
+
+        if (format.equals(ChartExportHelper.FORMAT_PNG)) {
+            context.putContextValue("chart.image.format", "png");
+
+            ChartExportHelper.exportImage(
+                out,
+                chart,
+                context);
+        }
+        else if (format.equals(ChartExportHelper.FORMAT_PDF)) {
+            context.putContextValue("chart.marginLeft", 5f);
+            context.putContextValue("chart.marginRight", 5f);
+            context.putContextValue("chart.marginTop", 5f);
+            context.putContextValue("chart.marginBottom", 5f);
+            context.putContextValue(
+                "chart.page.format",
+                ChartExportHelper.DEFAULT_PAGE_SIZE);
+
+            ChartExportHelper.exportPDF(
+                out,
+                chart,
+                context);
+        }
+        else if (format.equals(ChartExportHelper.FORMAT_SVG)) {
+            context.putContextValue(
+                "chart.encoding",
+                ChartExportHelper.DEFAULT_ENCODING);
+
+            ChartExportHelper.exportSVG(
+                out,
+                chart,
+                context);
+        }
+    }
+
+
+    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(plot);
+        addAnnotations(plot);
+        addSubtitles(chart);
+        adjustPlot(plot);
+        localizeAxes(plot);
+
+        removeEmptyRangeAxes(plot);
+        adjustAxes(plot);
+
+        preparePointRanges(plot);
+        autoZoom(plot);
+
+        applyThemes(plot);
+
+        return chart;
+    }
+
+
+    /**
+     * Add first and second dataset to plot.
+     * @param plot plot to add datasets to.
+     */
+    protected void addDatasets(XYPlot plot) {
+        if (first != null) {
+            logger.debug("Set the first axis dataset.");
+            plot.setDataset(0, first);
+        }
+        if (second != null) {
+            logger.debug("Set the second axis dataset.");
+            plot.setDataset(1, second);
+        }
+    }
+
+
+    public void addFirstAxisSeries(XYSeries series, boolean visible) {
+        if (first == null) {
+            first = new XYSeriesCollection();
+        }
+
+        if (series != null) {
+            if (visible) {
+                first.addSeries(series);
+            }
+
+            combineYRanges(new Range(series.getMinY(), series.getMaxY()), 0);
+            combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0);
+        }
+    }
+
+
+    public void addSecondAxisSeries(XYSeries series, boolean visible) {
+        if (second == null) {
+            second = new XYSeriesCollection();
+        }
+
+        if (series != null) {
+            if (visible) {
+                second.addSeries(series);
+            }
+
+            combineYRanges(new Range(series.getMinY(), series.getMaxY()), 1);
+            combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0);
+        }
+    }
+
+
+    private void combineXRanges(Range range, int index) {
+        Integer key = Integer.valueOf(index);
+
+        if (xRanges == null) {
+            xRanges = new HashMap<Integer, Range>();
+            xRanges.put(key, range);
+            return;
+        }
+
+        Range newX = null;
+        Range oldX = xRanges.get(key);
+
+        if (oldX != null) {
+            newX = Range.combine(oldX, range);
+        }
+        else {
+            newX = range;
+        }
+
+        xRanges.put(key, newX);
+    }
+
+
+    private void combineYRanges(Range range, int index) {
+        Integer key = Integer.valueOf(index);
+
+        if (yRanges == null) {
+            yRanges = new HashMap<Integer, Range>();
+            yRanges.put(key, range);
+            return;
+        }
+
+        Range newY = null;
+        Range oldY = yRanges.get(key);
+
+        if (oldY != null) {
+            newY = Range.combine(oldY, range);
+        }
+        else {
+            newY = range;
+        }
+
+        yRanges.put(key, newY);
+    }
+
+
+    /**
+     * Adds annotations to list (if visible is true).
+     */
+    public void addAnnotations(FLYSAnnotation annotation, boolean visible) {
+        if (!visible) {
+            return;
+        }
+
+        if (annotations == null) {
+            annotations = new ArrayList<FLYSAnnotation>();
+        }
+
+        annotations.add(annotation);
+    }
+
+
+    private void removeEmptyRangeAxes(XYPlot plot) {
+        if (first == null) {
+            plot.setRangeAxis(0, null);
+        }
+
+        if (second == null) {
+            plot.setRangeAxis(1, null);
+        }
+    }
+
+
+    private void preparePointRanges(XYPlot plot) {
+        for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
+            Integer key = Integer.valueOf(i);
+
+            Range r = xRanges.get(key);
+            if (r != null && r.getLowerBound() == r.getUpperBound()) {
+                xRanges.put(key, expandRange(r, 5));
+            }
+        }
+
+        for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            Integer key = Integer.valueOf(i);
+
+            Range r = yRanges.get(key);
+            if (r != null && r.getLowerBound() == r.getUpperBound()) {
+                yRanges.put(key, expandRange(r, 5));
+            }
+        }
+    }
+
+
+    public static Range expandRange(Range range, double percent) {
+        if (range == null) {
+            return null;
+        }
+
+        double value  = range.getLowerBound();
+        double expand = value / 100 * percent;
+
+        return expand != 0
+            ? new Range(value-expand, value+expand)
+            : new Range(-0.01 * percent, 0.01 * percent);
+    }
+
+
+    /**
+     * 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();
+
+        zoomX(plot, plot.getDomainAxis(), xRanges.get(0), xrange);
+
+        for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            ValueAxis yaxis = plot.getRangeAxis(i);
+
+            if (yaxis == null) {
+                logger.debug("Zoom problem: no Y Axis for index: " + i);
+                continue;
+            }
+
+            logger.debug("Prepare zoom settings for y axis at index: " + i);
+            zoomY(plot, yaxis, yRanges.get(Integer.valueOf(i)), 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 (range == null) {
+            return false;
+        }
+
+        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
+     * which are stored in <i>xRanges</i> and <i>yRanges</i>.
+     *
+     * @param index The index of the y-Axis.
+     *
+     * @return a Range[] as follows: [x-Range, y-Range].
+     */
+    public Range[] getRangesForDataset(int index) {
+        return new Range[] {
+            xRanges.get(Integer.valueOf(0)),
+            yRanges.get(Integer.valueOf(index))
+        };
+    }
+
+
+    protected void addAnnotations(XYPlot plot) {
+        plot.clearAnnotations();
+
+        if (annotations == null) {
+            logger.debug("No Annotations given.");
+            return;
+        }
+
+        LegendItemCollection lic = new LegendItemCollection();
+
+        int idx = 0;
+        if (plot.getRangeAxis(idx) == null && plot.getRangeAxisCount() >= 2) {
+            idx = 1;
+        }
+
+        XYItemRenderer renderer = plot.getRenderer(idx);
+
+        for (FLYSAnnotation fa: annotations) {
+            Document theme = fa.getTheme();
+
+            ThemeAccess themeAccess = new ThemeAccess(theme);
+
+            Color color   = themeAccess.parseLineColorField();
+            int lineWidth = themeAccess.parseLineWidth();
+
+            lic.add(new LegendItem(fa.getLabel(), color));
+
+            for (XYTextAnnotation ta: fa.getAnnotations()) {
+                if(ta instanceof StickyAxisAnnotation) {
+                    StickyAxisAnnotation sta = (StickyAxisAnnotation)ta;
+                    sta.applyTheme(themeAccess);
+                    renderer.addAnnotation(sta);
+                }
+                else {
+                    ta.setPaint(color);
+                    ta.setOutlineStroke(new BasicStroke((float) lineWidth));
+                    renderer.addAnnotation(ta);
+                }
+            }
+
+            plot.setFixedLegendItems(lic);
+        }
+    }
+
+
+    /**
+     * 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));
+
+        if (plot.getDataset(0) != null) {
+            plot.mapDatasetToRangeAxis(0, 0);
+        }
+
+        if (plot.getDataset(1) != null) {
+            plot.mapDatasetToRangeAxis(1, 1);
+        }
+    }
+
+
+    protected void addSubtitles(JFreeChart chart) {
+        // override this method in subclasses that need subtitles
+    }
+
+
+    /**
+     * This method walks over all axes (domain and range) of <i>plot</i> and
+     * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for
+     * range axes.
+     *
+     * @param plot The XYPlot.
+     */
+    private void localizeAxes(XYPlot plot) {
+        for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
+            ValueAxis axis = plot.getDomainAxis(i);
+
+            if (axis != null) {
+                localizeDomainAxis(axis);
+            }
+            else {
+                logger.warn("Domain axis at " + i + " is null.");
+            }
+        }
+
+        for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            ValueAxis axis = plot.getRangeAxis(i);
+
+            if (axis != null) {
+                localizeRangeAxis(axis);
+            }
+            else {
+                logger.warn("Range axis at " + i + " is null.");
+            }
+        }
+    }
+
+
+    /**
+     * Overrides the NumberFormat with the NumberFormat for the current locale
+     * that is provided by getLocale().
+     *
+     * @param domainAxis The domain axis that needs localization.
+     */
+    protected void localizeDomainAxis(ValueAxis domainAxis) {
+        NumberFormat nf = NumberFormat.getInstance(getLocale());
+        ((NumberAxis) domainAxis).setNumberFormatOverride(nf);
+    }
+
+
+    /**
+     * Overrides the NumberFormat with the NumberFormat for the current locale
+     * that is provided by getLocale().
+     *
+     * @param domainAxis The domain axis that needs localization.
+     */
+    protected void localizeRangeAxis(ValueAxis rangeAxis) {
+        NumberFormat nf = NumberFormat.getInstance(getLocale());
+        ((NumberAxis) rangeAxis).setNumberFormatOverride(nf);
+    }
+
+
+    protected void applyThemes(XYPlot plot) {
+        if (first != null) {
+            applyThemes(plot, first, 0);
+        }
+
+        if (second != null) {
+            applyThemes(plot, second, 1);
+        }
+    }
+
+
+    protected void applyThemes(XYPlot plot, XYSeriesCollection dataset, int i) {
+        LegendItemCollection lic  = new LegendItemCollection();
+        LegendItemCollection anno = plot.getFixedLegendItems();
+
+        XYLineAndShapeRenderer r = getRenderer(plot, i);
+
+        for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) {
+            XYSeries series = dataset.getSeries(s);
+
+            if (series instanceof StyledXYSeries) {
+                ((StyledXYSeries) series).applyTheme(r, s);
+            }
+
+            // special case: if there is just one single item, we need to enable
+            // points for this series, otherwise we would not see anything in
+            // the chart area.
+            if (series.getItemCount() == 1) {
+                r.setSeriesShapesVisible(s, true);
+            }
+
+            lic.add(r.getLegendItem(i, s));
+        }
+
+        if (anno != null) {
+            lic.addAll(anno);
+        }
+
+        plot.setFixedLegendItems(lic);
+
+        plot.setRenderer(i, r);
+    }
+
+
+    protected XYLineAndShapeRenderer getRenderer(XYPlot plot, int idx) {
+        XYLineAndShapeRenderer r =
+            (XYLineAndShapeRenderer) plot.getRenderer(idx);
+
+        if (r != null) {
+            return r;
+        }
+
+        if (idx == 0) {
+            logger.warn("No default renderer set!");
+            return new XYLineAndShapeRenderer();
+        }
+
+        r = (XYLineAndShapeRenderer) plot.getRenderer(0);
+
+        try {
+            return (XYLineAndShapeRenderer) r.clone();
+        }
+        catch (CloneNotSupportedException cnse) {
+            logger.warn(cnse, cnse);
+        }
+
+        logger.warn("No applicalable renderer found!");
+
+        return new XYLineAndShapeRenderer();
+    }
+}
+// vim: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/java2d/NOPGraphics2D.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,528 @@
+package de.intevation.flys.java2d;
+
+import java.util.Map;
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.Image;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Paint;
+import java.awt.GraphicsConfiguration;
+import java.awt.Stroke;
+import java.awt.Rectangle;
+import java.awt.Font;
+import java.awt.FontMetrics;
+
+import java.awt.image.RenderedImage;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+
+import java.awt.image.renderable.RenderableImage;
+
+import java.awt.geom.AffineTransform;
+
+import java.text.AttributedCharacterIterator;
+
+import java.awt.font.GlyphVector;
+import java.awt.font.FontRenderContext;
+
+import java.awt.RenderingHints;
+
+public final class NOPGraphics2D
+extends            Graphics2D
+{
+    private Graphics2D parent;
+
+    public NOPGraphics2D(Graphics2D parent) {
+        this.parent = parent;
+    }
+
+    @Override
+    public final void addRenderingHints(Map<?,?> hints) {
+        parent.addRenderingHints(hints);
+    }
+
+    @Override
+    public final void clip(Shape s) {
+    }
+
+    @Override
+    public final void draw(Shape s) {
+    }
+
+    @Override
+    public final void drawGlyphVector(GlyphVector g, float x, float y) {
+    }
+
+    @Override
+    public final void drawImage(
+        BufferedImage   img,
+        BufferedImageOp op,
+        int x,
+        int y
+    ) {
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image           img,
+        AffineTransform xform,
+        ImageObserver   obs
+    ) {
+        return true;
+    }
+
+    @Override
+    public final void drawRenderableImage(
+        RenderableImage img,
+        AffineTransform xform
+    ) {
+    }
+
+    @Override
+    public final void drawRenderedImage(
+        RenderedImage   img,
+        AffineTransform xform
+    ) {
+    }
+
+    @Override
+    public final void drawString(
+        AttributedCharacterIterator iterator,
+        float x,
+        float y
+    ) {
+    }
+
+    @Override
+    public final void drawString(
+        AttributedCharacterIterator iterator,
+        int x,
+        int y
+    ) {
+    }
+
+    @Override
+    public final void drawString(String str, float x, float y) {
+    }
+
+    @Override
+    public final void drawString(String str, int x, int y) {
+    }
+
+    @Override
+    public final void fill(Shape s) {
+    }
+
+    @Override
+    public final Color getBackground() {
+        return parent.getBackground();
+    }
+
+    @Override
+    public final Composite getComposite() {
+        return parent.getComposite();
+    }
+
+    @Override
+    public final GraphicsConfiguration getDeviceConfiguration() {
+        return parent.getDeviceConfiguration();
+    }
+
+    @Override
+    public final FontRenderContext getFontRenderContext() {
+        return parent.getFontRenderContext();
+    }
+
+    @Override
+    public final Paint getPaint() {
+        return parent.getPaint();
+    }
+
+    @Override
+    public final Object getRenderingHint(RenderingHints.Key hintKey) {
+        return parent.getRenderingHint(hintKey);
+    }
+
+    @Override
+    public final RenderingHints getRenderingHints() {
+        return parent.getRenderingHints();
+    }
+
+    @Override
+    public final Stroke getStroke() {
+        return parent.getStroke();
+    }
+
+    @Override
+    public final AffineTransform getTransform() {
+        return parent.getTransform();
+    }
+
+    @Override
+    public final boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+        return parent.hit(rect, s, onStroke);
+    }
+
+    @Override
+    public final void rotate(double theta) {
+        parent.rotate(theta);
+    }
+
+    @Override
+    public final void rotate(double theta, double x, double y) {
+        parent.rotate(theta);
+    }
+
+    @Override
+    public final void scale(double sx, double sy) {
+        parent.scale(sx, sy);
+    }
+
+    @Override
+    public final void setBackground(Color color) {
+        parent.setBackground(color);
+    }
+
+    @Override
+    public final void setComposite(Composite comp) {
+        parent.setComposite(comp);
+    }
+
+    @Override
+    public final void setPaint(Paint paint) {
+        parent.setPaint(paint);
+    }
+
+    @Override
+    public final void setRenderingHint(
+        RenderingHints.Key hintKey,
+        Object             hintValue
+    ) {
+        parent.setRenderingHint(hintKey, hintValue);
+    }
+
+    @Override
+    public final void setRenderingHints(Map<?,?> hints) {
+        parent.setRenderingHints(hints);
+    }
+
+    @Override
+    public final void setStroke(Stroke s) {
+        parent.setStroke(s);
+    }
+
+    @Override
+    public final void setTransform(AffineTransform Tx) {
+        parent.setTransform(Tx);
+    }
+
+
+    @Override
+    public final void shear(double shx, double shy) {
+        parent.shear(shx, shy);
+    }
+
+    @Override
+    public final void transform(AffineTransform Tx) {
+        parent.transform(Tx);
+    }
+
+    @Override
+    public final void translate(double tx, double ty) {
+        parent.translate(tx, ty);
+    }
+
+    @Override
+    public final void translate(int tx, int ty) {
+        parent.translate(tx, ty);
+    }
+
+    @Override
+    public final void dispose() {
+        parent.dispose();
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        int width,
+        int height,
+        Color bgcolor,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int dx1,
+        int dy1,
+        int dx2,
+        int dy2,
+        int sx1,
+        int sy1,
+        int sx2,
+        int sy2,
+        Color bgcolor,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int dx1,
+        int dy1,
+        int dx2,
+        int dy2,
+        int sx1,
+        int sy1,
+        int sx2,
+        int sy2,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        Color bgcolor,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        int width,
+        int height,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final boolean drawImage(
+        Image img,
+        int x,
+        int y,
+        ImageObserver observer
+    ) {
+        return true;
+    }
+
+    @Override
+    public final void fillPolygon(
+        int [] xPoints,
+        int [] yPoints,
+        int    nPoints
+    ) {
+    }
+
+    @Override
+    public final void drawPolygon(
+        int [] xPoints,
+        int [] yPoints,
+        int    nPoints
+    ) {
+    }
+
+    @Override
+    public final void drawPolyline(
+        int [] xPoints,
+        int [] yPoints,
+        int    nPoints
+    ) {
+    }
+
+    @Override
+    public final void fillArc(
+        int x,
+        int y,
+        int width,
+        int height,
+        int startAngle,
+        int arcAngle
+    ) {
+    }
+
+    @Override
+    public final void drawArc(
+        int x,
+        int y,
+        int width,
+        int height,
+        int startAngle,
+        int arcAngle
+    ) {
+    }
+
+    @Override
+    public final void fillOval(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void drawOval(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void fillRoundRect(
+        int x,
+        int y,
+        int width,
+        int height,
+        int arcWidth,
+        int arcHeight
+    ) {
+    }
+
+    @Override
+    public final void drawRoundRect(
+        int x,
+        int y,
+        int width,
+        int height,
+        int arcWidth,
+        int arcHeight
+    ) {
+    }
+
+    @Override
+    public final void clearRect(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void fillRect(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+    }
+
+    @Override
+    public final void drawLine(
+        int x1,
+        int y1,
+        int x2,
+        int y2
+    ) {
+    }
+
+    @Override
+    public final void copyArea(
+        int x,
+        int y,
+        int width,
+        int height,
+        int dx,
+        int dy
+    ) {
+    }
+
+    @Override
+    public final void setClip(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+        parent.setClip(x, y, width, height);
+    }
+
+    @Override
+    public final void setClip(Shape shape) {
+        parent.setClip(shape);
+    }
+
+    @Override
+    public final Shape getClip() {
+        return parent.getClip();
+    }
+
+    @Override
+    public final void clipRect(
+        int x,
+        int y,
+        int width,
+        int height
+    ) {
+        parent.clipRect(x, y, width, height);
+    }
+
+    @Override
+    public final Rectangle getClipBounds() {
+        return parent.getClipBounds();
+    }
+
+    @Override
+    public final FontMetrics getFontMetrics(Font f) {
+        return parent.getFontMetrics(f);
+    }
+
+    @Override
+    public final void setFont(Font font) {
+        parent.setFont(font);
+    }
+
+    @Override
+    public final Font getFont() {
+        return parent.getFont();
+    }
+
+    @Override
+    public final void setXORMode(Color c1) {
+        parent.setXORMode(c1);
+    }
+
+    @Override
+    public final void setPaintMode() {
+        parent.setPaintMode();
+    }
+
+    @Override
+    public final void setColor(Color c) {
+        parent.setColor(c);
+    }
+
+    @Override
+    public final Color getColor() {
+        return parent.getColor();
+    }
+
+    @Override
+    public final Graphics create() {
+        return new NOPGraphics2D((Graphics2D)parent.create());
+    }
+}
+// vim: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/jfree/FLYSAnnotation.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,46 @@
+package de.intevation.flys.jfree;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+/**
+ * List of Annotations with name and theme.
+ */
+public class FLYSAnnotation {
+
+    protected List<XYTextAnnotation> annotations;
+
+    protected Document theme;
+
+    protected String label;
+
+
+    public FLYSAnnotation(String label, List<XYTextAnnotation> annotations) {
+        this.label       = label;
+        this.annotations = annotations;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public List<XYTextAnnotation> getAnnotations() {
+        return annotations;
+    }
+
+    public void setTheme(Document theme) {
+        this.theme = theme;
+    }
+
+    public Document getTheme() {
+        return theme;
+    }
+}
+// vim: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/jfree/StableXYDifferenceRenderer.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,1271 @@
+/* ===========================================================
+ * JFreeChart : a free chart library for the Java(tm) platform
+ * ===========================================================
+ *
+ * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
+ *
+ * Project Info:  http://www.jfree.org/jfreechart/index.html
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
+ * in the United States and other countries.]
+ *
+ * -------------------------
+ * StableXYDifferenceRenderer.java
+ * -------------------------
+ * (C) Copyright 2003-2008, by Object Refinery Limited and Contributors.
+ *
+ * Original Author:  David Gilbert (for Object Refinery Limited);
+ * Contributor(s):   Richard West, Advanced Micro Devices, Inc. (major rewrite
+ *                   of difference drawing algorithm);
+ *
+ * Changes:
+ * --------
+ * 30-Apr-2003 : Version 1 (DG);
+ * 30-Jul-2003 : Modified entity constructor (CZ);
+ * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
+ * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
+ * 09-Feb-2004 : Updated to support horizontal plot orientation (DG);
+ * 10-Feb-2004 : Added default constructor, setter methods and updated
+ *               Javadocs (DG);
+ * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
+ * 30-Mar-2004 : Fixed bug in getNegativePaint() method (DG);
+ * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
+ *               getYValue() (DG);
+ * 25-Aug-2004 : Fixed a bug preventing the use of crosshairs (DG);
+ * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
+ * 19-Jan-2005 : Now accesses only primitive values from dataset (DG);
+ * 22-Feb-2005 : Override getLegendItem(int, int) to return "line" items (DG);
+ * 13-Apr-2005 : Fixed shape positioning bug (id = 1182062) (DG);
+ * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
+ * 04-May-2005 : Override equals() method, renamed get/setPlotShapes() -->
+ *               get/setShapesVisible (DG);
+ * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG);
+ * 16-Jun-2005 : Fix bug (1221021) affecting stroke used for each series (DG);
+ * ------------- JFREECHART 1.0.x ---------------------------------------------
+ * 24-Jan-2007 : Added flag to allow rounding of x-coordinates, and fixed
+ *               bug in clone() (DG);
+ * 05-Feb-2007 : Added an extra call to updateCrosshairValues() in
+ *               drawItemPass1(), to fix bug 1564967 (DG);
+ * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
+ * 08-Mar-2007 : Fixed entity generation (DG);
+ * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
+ * 23-Apr-2007 : Rewrite of difference drawing algorithm to allow use of
+ *               series with disjoint x-values (RW);
+ * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG);
+ * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
+ * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
+ * 05-Nov-2007 : Draw item labels if visible (RW);
+ * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG);
+ *
+ */
+package de.intevation.flys.jfree;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Iterator;
+
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.entity.XYItemEntity;
+import org.jfree.chart.event.RendererChangeEvent;
+import org.jfree.chart.labels.XYToolTipGenerator;
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.urls.XYURLGenerator;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.io.SerialUtilities;
+import org.jfree.ui.RectangleEdge;
+import org.jfree.util.PaintUtilities;
+import org.jfree.util.PublicCloneable;
+import org.jfree.util.ShapeUtilities;
+
+import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRendererState;
+
+/**
+ * A renderer for an {@link XYPlot} that highlights the differences between two
+ * series.  The example shown here is generated by the
+ * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart
+ * demo collection:
+ * <br><br>
+ * <img src="../../../../../images/StableXYDifferenceRendererSample.png"
+ * alt="StableXYDifferenceRendererSample.png" />
+ */
+public class StableXYDifferenceRenderer extends AbstractXYItemRenderer
+        implements XYItemRenderer, PublicCloneable {
+
+    /** For serialization. */
+    private static final long serialVersionUID = -8447915602375584857L;
+
+    /** The paint used to highlight positive differences (y(0) > y(1)). */
+    private transient Paint positivePaint;
+
+    /** The paint used to highlight negative differences (y(0) < y(1)). */
+    private transient Paint negativePaint;
+
+    /** Display shapes at each point? */
+    private boolean shapesVisible;
+
+    /** The shape to display in the legend item. */
+    private transient Shape legendLine;
+
+    private XYDatasetToZeroMapper mapper;
+
+    /**
+     * This flag controls whether or not the x-coordinates (in Java2D space)
+     * are rounded to integers.  When set to true, this can avoid the vertical
+     * striping that anti-aliasing can generate.  However, the rounding may not
+     * be appropriate for output in high resolution formats (for example,
+     * vector graphics formats such as SVG and PDF).
+     *
+     * @since 1.0.4
+     */
+    private boolean roundXCoordinates;
+
+    /**
+     * Creates a new renderer with default attributes.
+     */
+    public StableXYDifferenceRenderer() {
+        this(Color.green, Color.red, false, null);
+    }
+
+    /**
+     * Creates a new renderer.
+     *
+     * @param positivePaint  the highlight color for positive differences
+     *                       (<code>null</code> not permitted).
+     * @param negativePaint  the highlight color for negative differences
+     *                       (<code>null</code> not permitted).
+     * @param shapes  draw shapes?
+     */
+    public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
+                                boolean shapes,
+                                XYDatasetToZeroMapper mapper) {
+        if (positivePaint == null) {
+            throw new IllegalArgumentException(
+                    "Null 'positivePaint' argument.");
+        }
+        if (negativePaint == null) {
+            throw new IllegalArgumentException(
+                    "Null 'negativePaint' argument.");
+        }
+        this.positivePaint = positivePaint;
+        this.negativePaint = negativePaint;
+        this.shapesVisible = shapes;
+        this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0);
+        this.roundXCoordinates = false;
+        this.mapper = mapper;
+    }
+
+    /**
+     * Returns the paint used to highlight positive differences.
+     *
+     * @return The paint (never <code>null</code>).
+     *
+     * @see #setPositivePaint(Paint)
+     */
+    public Paint getPositivePaint() {
+        return this.positivePaint;
+    }
+
+    /**
+     * Sets the paint used to highlight positive differences and sends a
+     * {@link RendererChangeEvent} to all registered listeners.
+     *
+     * @param paint  the paint (<code>null</code> not permitted).
+     *
+     * @see #getPositivePaint()
+     */
+    public void setPositivePaint(Paint paint) {
+        if (paint == null) {
+            throw new IllegalArgumentException("Null 'paint' argument.");
+        }
+        this.positivePaint = paint;
+        fireChangeEvent();
+    }
+
+    /**
+     * Returns the paint used to highlight negative differences.
+     *
+     * @return The paint (never <code>null</code>).
+     *
+     * @see #setNegativePaint(Paint)
+     */
+    public Paint getNegativePaint() {
+        return this.negativePaint;
+    }
+
+    /**
+     * Sets the paint used to highlight negative differences.
+     *
+     * @param paint  the paint (<code>null</code> not permitted).
+     *
+     * @see #getNegativePaint()
+     */
+    public void setNegativePaint(Paint paint) {
+        if (paint == null) {
+            throw new IllegalArgumentException("Null 'paint' argument.");
+        }
+        this.negativePaint = paint;
+        notifyListeners(new RendererChangeEvent(this));
+    }
+
+    /**
+     * Returns a flag that controls whether or not shapes are drawn for each
+     * data value.
+     *
+     * @return A boolean.
+     *
+     * @see #setShapesVisible(boolean)
+     */
+    public boolean getShapesVisible() {
+        return this.shapesVisible;
+    }
+
+    /**
+     * Sets a flag that controls whether or not shapes are drawn for each
+     * data value, and sends a {@link RendererChangeEvent} to all registered
+     * listeners.
+     *
+     * @param flag  the flag.
+     *
+     * @see #getShapesVisible()
+     */
+    public void setShapesVisible(boolean flag) {
+        this.shapesVisible = flag;
+        fireChangeEvent();
+    }
+
+    /**
+     * Returns the shape used to represent a line in the legend.
+     *
+     * @return The legend line (never <code>null</code>).
+     *
+     * @see #setLegendLine(Shape)
+     */
+    public Shape getLegendLine() {
+        return this.legendLine;
+    }
+
+    /**
+     * Sets the shape used as a line in each legend item and sends a
+     * {@link RendererChangeEvent} to all registered listeners.
+     *
+     * @param line  the line (<code>null</code> not permitted).
+     *
+     * @see #getLegendLine()
+     */
+    public void setLegendLine(Shape line) {
+        if (line == null) {
+            throw new IllegalArgumentException("Null 'line' argument.");
+        }
+        this.legendLine = line;
+        fireChangeEvent();
+    }
+
+    /**
+     * Returns the flag that controls whether or not the x-coordinates (in
+     * Java2D space) are rounded to integer values.
+     *
+     * @return The flag.
+     *
+     * @since 1.0.4
+     *
+     * @see #setRoundXCoordinates(boolean)
+     */
+    public boolean getRoundXCoordinates() {
+        return this.roundXCoordinates;
+    }
+
+    /**
+     * Sets the flag that controls whether or not the x-coordinates (in
+     * Java2D space) are rounded to integer values, and sends a
+     * {@link RendererChangeEvent} to all registered listeners.
+     *
+     * @param round  the new flag value.
+     *
+     * @since 1.0.4
+     *
+     * @see #getRoundXCoordinates()
+     */
+    public void setRoundXCoordinates(boolean round) {
+        this.roundXCoordinates = round;
+        fireChangeEvent();
+    }
+
+    /**
+     * Initialises the renderer and returns a state object that should be
+     * passed to subsequent calls to the drawItem() method.  This method will
+     * be called before the first item is rendered, giving the renderer an
+     * opportunity to initialise any state information it wants to maintain.
+     * The renderer can do nothing if it chooses.
+     *
+     * @param g2  the graphics device.
+     * @param dataArea  the area inside the axes.
+     * @param plot  the plot.
+     * @param data  the data.
+     * @param info  an optional info collection object to return data back to
+     *              the caller.
+     *
+     * @return A state object.
+     */
+    public XYItemRendererState initialise(Graphics2D g2,
+                                          Rectangle2D dataArea,
+                                          XYPlot plot,
+                                          XYDataset data,
+                                          PlotRenderingInfo info) {
+
+        XYItemRendererState state = super.initialise(g2, dataArea, plot, data,
+                info);
+        state.setProcessVisibleItemsOnly(false);
+        return state;
+
+    }
+
+    /**
+     * Returns <code>2</code>, the number of passes required by the renderer.
+     * The {@link XYPlot} will run through the dataset this number of times.
+     *
+     * @return The number of passes required by the renderer.
+     */
+    public int getPassCount() {
+        return 2;
+    }
+
+    /**
+     * Draws the visual representation of a single data item.
+     *
+     * @param g2  the graphics device.
+     * @param state  the renderer state.
+     * @param dataArea  the area within which the data is being drawn.
+     * @param info  collects information about the drawing.
+     * @param plot  the plot (can be used to obtain standard color
+     *              information etc).
+     * @param domainAxis  the domain (horizontal) axis.
+     * @param rangeAxis  the range (vertical) axis.
+     * @param dataset  the dataset.
+     * @param series  the series index (zero-based).
+     * @param item  the item index (zero-based).
+     * @param crosshairState  crosshair information for the plot
+     *                        (<code>null</code> permitted).
+     * @param pass  the pass index.
+     */
+    public void drawItem(Graphics2D g2,
+                         XYItemRendererState state,
+                         Rectangle2D dataArea,
+                         PlotRenderingInfo info,
+                         XYPlot plot,
+                         ValueAxis domainAxis,
+                         ValueAxis rangeAxis,
+                         XYDataset dataset,
+                         int series,
+                         int item,
+                         CrosshairState crosshairState,
+                         int pass) {
+
+        if (mapper == null) {
+            switch (pass) {
+                case 0:
+                    drawItemPass0(g2, dataArea, info, 
+                        plot, domainAxis, rangeAxis,
+                        dataset, series, item, crosshairState);
+                    break;
+                case 1:
+                    drawItemPass1(g2, dataArea, info,
+                        plot, domainAxis, rangeAxis,
+                        dataset, series, item, crosshairState);
+            }
+        }
+        else {
+            for (Iterator<XYDataset> iter = mapper.iterator(dataset);
+                iter.hasNext();
+            ) {
+                switch (pass) {
+                    case 0:
+                        drawItemPass0(g2, dataArea, info, 
+                            plot, domainAxis, rangeAxis,
+                            dataset, series, item, crosshairState);
+                        break;
+                    case 1:
+                        drawItemPass1(g2, dataArea, info,
+                            plot, domainAxis, rangeAxis,
+                            dataset, series, item, crosshairState);
+                }
+            } // for all segments
+        }
+
+    }
+
+    /**
+     * Draws the visual representation of a single data item, first pass.
+     *
+     * @param x_graphics  the graphics device.
+     * @param x_dataArea  the area within which the data is being drawn.
+     * @param x_info  collects information about the drawing.
+     * @param x_plot  the plot (can be used to obtain standard color
+     *                information etc).
+     * @param x_domainAxis  the domain (horizontal) axis.
+     * @param x_rangeAxis  the range (vertical) axis.
+     * @param x_dataset  the dataset.
+     * @param x_series  the series index (zero-based).
+     * @param x_item  the item index (zero-based).
+     * @param x_crosshairState  crosshair information for the plot
+     *                          (<code>null</code> permitted).
+     */
+    protected void drawItemPass0(Graphics2D x_graphics,
+                                 Rectangle2D x_dataArea,
+                                 PlotRenderingInfo x_info,
+                                 XYPlot x_plot,
+                                 ValueAxis x_domainAxis,
+                                 ValueAxis x_rangeAxis,
+                                 XYDataset x_dataset,
+                                 int x_series,
+                                 int x_item,
+                                 CrosshairState x_crosshairState) {
+
+        if (!((0 == x_series) && (0 == x_item))) {
+            return;
+        }
+
+        boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount());
+
+        // check if either series is a degenerate case (i.e. less than 2 points)
+        if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) {
+            return;
+        }
+
+        // check if series are disjoint (i.e. domain-spans do not overlap)
+        if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) {
+            return;
+        }
+
+        // polygon definitions
+        LinkedList l_minuendXs    = new LinkedList();
+        LinkedList l_minuendYs    = new LinkedList();
+        LinkedList l_subtrahendXs = new LinkedList();
+        LinkedList l_subtrahendYs = new LinkedList();
+        LinkedList l_polygonXs    = new LinkedList();
+        LinkedList l_polygonYs    = new LinkedList();
+
+        // state
+        int l_minuendItem      = 0;
+        int l_minuendItemCount = x_dataset.getItemCount(0);
+        Double l_minuendCurX   = null;
+        Double l_minuendNextX  = null;
+        Double l_minuendCurY   = null;
+        Double l_minuendNextY  = null;
+        double l_minuendMaxY   = Double.NEGATIVE_INFINITY;
+        double l_minuendMinY   = Double.POSITIVE_INFINITY;
+
+        int l_subtrahendItem      = 0;
+        int l_subtrahendItemCount = 0; // actual value set below
+        Double l_subtrahendCurX   = null;
+        Double l_subtrahendNextX  = null;
+        Double l_subtrahendCurY   = null;
+        Double l_subtrahendNextY  = null;
+        double l_subtrahendMaxY   = Double.NEGATIVE_INFINITY;
+        double l_subtrahendMinY   = Double.POSITIVE_INFINITY;
+
+        // if a subtrahend is not specified, assume it is zero
+        if (b_impliedZeroSubtrahend) {
+            l_subtrahendItem      = 0;
+            l_subtrahendItemCount = 2;
+            l_subtrahendCurX      = new Double(x_dataset.getXValue(0, 0));
+            l_subtrahendNextX     = new Double(x_dataset.getXValue(0,
+                    (l_minuendItemCount - 1)));
+            l_subtrahendCurY      = new Double(0.0);
+            l_subtrahendNextY     = new Double(0.0);
+            l_subtrahendMaxY      = 0.0;
+            l_subtrahendMinY      = 0.0;
+
+            l_subtrahendXs.add(l_subtrahendCurX);
+            l_subtrahendYs.add(l_subtrahendCurY);
+        }
+        else {
+            l_subtrahendItemCount = x_dataset.getItemCount(1);
+        }
+
+        boolean b_minuendDone           = false;
+        boolean b_minuendAdvanced       = true;
+        boolean b_minuendAtIntersect    = false;
+        boolean b_minuendFastForward    = false;
+        boolean b_subtrahendDone        = false;
+        boolean b_subtrahendAdvanced    = true;
+        boolean b_subtrahendAtIntersect = false;
+        boolean b_subtrahendFastForward = false;
+        boolean b_colinear              = false;
+
+        boolean b_positive;
+
+        // coordinate pairs
+        double l_x1 = 0.0, l_y1 = 0.0; // current minuend point
+        double l_x2 = 0.0, l_y2 = 0.0; // next minuend point
+        double l_x3 = 0.0, l_y3 = 0.0; // current subtrahend point
+        double l_x4 = 0.0, l_y4 = 0.0; // next subtrahend point
+
+        // fast-forward through leading tails
+        boolean b_fastForwardDone = false;
+        while (!b_fastForwardDone) {
+            // get the x and y coordinates
+            l_x1 = x_dataset.getXValue(0, l_minuendItem);
+            l_y1 = x_dataset.getYValue(0, l_minuendItem);
+            l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
+            l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
+
+            l_minuendCurX  = new Double(l_x1);
+            l_minuendCurY  = new Double(l_y1);
+            l_minuendNextX = new Double(l_x2);
+            l_minuendNextY = new Double(l_y2);
+
+            if (b_impliedZeroSubtrahend) {
+                l_x3 = l_subtrahendCurX.doubleValue();
+                l_y3 = l_subtrahendCurY.doubleValue();
+                l_x4 = l_subtrahendNextX.doubleValue();
+                l_y4 = l_subtrahendNextY.doubleValue();
+            }
+            else {
+                l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
+                l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
+                l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
+                l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
+
+                l_subtrahendCurX  = new Double(l_x3);
+                l_subtrahendCurY  = new Double(l_y3);
+                l_subtrahendNextX = new Double(l_x4);
+                l_subtrahendNextY = new Double(l_y4);
+            }
+
+            if (l_x2 <= l_x3) {
+                // minuend needs to be fast forwarded
+                l_minuendItem++;
+                b_minuendFastForward = true;
+                continue;
+            }
+
+            if (l_x4 <= l_x1) {
+                // subtrahend needs to be fast forwarded
+                l_subtrahendItem++;
+                b_subtrahendFastForward = true;
+                continue;
+            }
+
+            // check if initial polygon needs to be clipped
+            if ((l_x3 < l_x1) && (l_x1 < l_x4)) {
+                // project onto subtrahend
+                double l_slope   = (l_y4 - l_y3) / (l_x4 - l_x3);
+                l_subtrahendCurX = l_minuendCurX;
+                l_subtrahendCurY = new Double((l_slope * l_x1)
+                        + (l_y3 - (l_slope * l_x3)));
+
+                l_subtrahendXs.add(l_subtrahendCurX);
+                l_subtrahendYs.add(l_subtrahendCurY);
+            }
+
+            if ((l_x1 < l_x3) && (l_x3 < l_x2)) {
+                // project onto minuend
+                double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
+                l_minuendCurX  = l_subtrahendCurX;
+                l_minuendCurY  = new Double((l_slope * l_x3)
+                        + (l_y1 - (l_slope * l_x1)));
+
+                l_minuendXs.add(l_minuendCurX);
+                l_minuendYs.add(l_minuendCurY);
+            }
+
+            l_minuendMaxY    = l_minuendCurY.doubleValue();
+            l_minuendMinY    = l_minuendCurY.doubleValue();
+            l_subtrahendMaxY = l_subtrahendCurY.doubleValue();
+            l_subtrahendMinY = l_subtrahendCurY.doubleValue();
+
+            b_fastForwardDone = true;
+        }
+
+        // start of algorithm
+        while (!b_minuendDone && !b_subtrahendDone) {
+            if (!b_minuendDone && !b_minuendFastForward && b_minuendAdvanced) {
+                l_x1 = x_dataset.getXValue(0, l_minuendItem);
+                l_y1 = x_dataset.getYValue(0, l_minuendItem);
+                l_minuendCurX = new Double(l_x1);
+                l_minuendCurY = new Double(l_y1);
+
+                if (!b_minuendAtIntersect) {
+                    l_minuendXs.add(l_minuendCurX);
+                    l_minuendYs.add(l_minuendCurY);
+                }
+
+                l_minuendMaxY = Math.max(l_minuendMaxY, l_y1);
+                l_minuendMinY = Math.min(l_minuendMinY, l_y1);
+
+                l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
+                l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
+                l_minuendNextX = new Double(l_x2);
+                l_minuendNextY = new Double(l_y2);
+            }
+
+            // never updated the subtrahend if it is implied to be zero
+            if (!b_impliedZeroSubtrahend && !b_subtrahendDone
+                    && !b_subtrahendFastForward && b_subtrahendAdvanced) {
+                l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
+                l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
+                l_subtrahendCurX = new Double(l_x3);
+                l_subtrahendCurY = new Double(l_y3);
+
+                if (!b_subtrahendAtIntersect) {
+                    l_subtrahendXs.add(l_subtrahendCurX);
+                    l_subtrahendYs.add(l_subtrahendCurY);
+                }
+
+                l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3);
+                l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3);
+
+                l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
+                l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
+                l_subtrahendNextX = new Double(l_x4);
+                l_subtrahendNextY = new Double(l_y4);
+            }
+
+            // deassert b_*FastForward (only matters for 1st time through loop)
+            b_minuendFastForward    = false;
+            b_subtrahendFastForward = false;
+
+            Double l_intersectX = null;
+            Double l_intersectY = null;
+            boolean b_intersect = false;
+
+            b_minuendAtIntersect    = false;
+            b_subtrahendAtIntersect = false;
+
+            // check for intersect
+            if ((l_x2 == l_x4) && (l_y2 == l_y4)) {
+                // check if line segments are colinear
+                if ((l_x1 == l_x3) && (l_y1 == l_y3)) {
+                    b_colinear = true;
+                }
+                else {
+                    // the intersect is at the next point for both the minuend
+                    // and subtrahend
+                    l_intersectX = new Double(l_x2);
+                    l_intersectY = new Double(l_y2);
+
+                    b_intersect             = true;
+                    b_minuendAtIntersect    = true;
+                    b_subtrahendAtIntersect = true;
+                 }
+            }
+            else {
+                // compute common denominator
+                double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1))
+                        - ((l_x4 - l_x3) * (l_y2 - l_y1));
+
+                // compute common deltas
+                double l_deltaY = l_y1 - l_y3;
+                double l_deltaX = l_x1 - l_x3;
+
+                // compute numerators
+                double l_numeratorA = ((l_x4 - l_x3) * l_deltaY)
+                        - ((l_y4 - l_y3) * l_deltaX);
+                double l_numeratorB = ((l_x2 - l_x1) * l_deltaY)
+                        - ((l_y2 - l_y1) * l_deltaX);
+
+                // check if line segments are colinear
+                if ((0 == l_numeratorA) && (0 == l_numeratorB)
+                        && (0 == l_denominator)) {
+                    b_colinear = true;
+                }
+                else {
+                    // check if previously colinear
+                    if (b_colinear) {
+                        // clear colinear points and flag
+                        l_minuendXs.clear();
+                        l_minuendYs.clear();
+                        l_subtrahendXs.clear();
+                        l_subtrahendYs.clear();
+                        l_polygonXs.clear();
+                        l_polygonYs.clear();
+
+                        b_colinear = false;
+
+                        // set new starting point for the polygon
+                        boolean b_useMinuend = ((l_x3 <= l_x1)
+                                && (l_x1 <= l_x4));
+                        l_polygonXs.add(b_useMinuend ? l_minuendCurX
+                                : l_subtrahendCurX);
+                        l_polygonYs.add(b_useMinuend ? l_minuendCurY
+                                : l_subtrahendCurY);
+                    }
+
+                    // compute slope components
+                    double l_slopeA = l_numeratorA / l_denominator;
+                    double l_slopeB = l_numeratorB / l_denominator;
+
+                    // check if the line segments intersect
+                    if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB)
+                            && (l_slopeB <= 1)) {
+                        // compute the point of intersection
+                        double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1));
+                        double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1));
+
+                        l_intersectX            = new Double(l_xi);
+                        l_intersectY            = new Double(l_yi);
+                        b_intersect             = true;
+                        b_minuendAtIntersect    = ((l_xi == l_x2)
+                                && (l_yi == l_y2));
+                        b_subtrahendAtIntersect = ((l_xi == l_x4)
+                                && (l_yi == l_y4));
+
+                        // advance minuend and subtrahend to intesect
+                        l_minuendCurX    = l_intersectX;
+                        l_minuendCurY    = l_intersectY;
+                        l_subtrahendCurX = l_intersectX;
+                        l_subtrahendCurY = l_intersectY;
+                    }
+                }
+            }
+
+            if (b_intersect) {
+                // create the polygon
+                // add the minuend's points to polygon
+                l_polygonXs.addAll(l_minuendXs);
+                l_polygonYs.addAll(l_minuendYs);
+
+                // add intersection point to the polygon
+                l_polygonXs.add(l_intersectX);
+                l_polygonYs.add(l_intersectY);
+
+                // add the subtrahend's points to the polygon in reverse
+                Collections.reverse(l_subtrahendXs);
+                Collections.reverse(l_subtrahendYs);
+                l_polygonXs.addAll(l_subtrahendXs);
+                l_polygonYs.addAll(l_subtrahendYs);
+
+                // create an actual polygon
+                b_positive = (l_subtrahendMaxY <= l_minuendMaxY)
+                        && (l_subtrahendMinY <= l_minuendMinY);
+                createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
+                        x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
+
+                // clear the point vectors
+                l_minuendXs.clear();
+                l_minuendYs.clear();
+                l_subtrahendXs.clear();
+                l_subtrahendYs.clear();
+                l_polygonXs.clear();
+                l_polygonYs.clear();
+
+                // set the maxY and minY values to intersect y-value
+                double l_y       = l_intersectY.doubleValue();
+                l_minuendMaxY    = l_y;
+                l_subtrahendMaxY = l_y;
+                l_minuendMinY    = l_y;
+                l_subtrahendMinY = l_y;
+
+                // add interection point to new polygon
+                l_polygonXs.add(l_intersectX);
+                l_polygonYs.add(l_intersectY);
+            }
+
+            // advance the minuend if needed
+            if (l_x2 <= l_x4) {
+                l_minuendItem++;
+                b_minuendAdvanced = true;
+            }
+            else {
+                b_minuendAdvanced = false;
+            }
+
+            // advance the subtrahend if needed
+            if (l_x4 <= l_x2) {
+                l_subtrahendItem++;
+                b_subtrahendAdvanced = true;
+            }
+            else {
+                b_subtrahendAdvanced = false;
+            }
+
+            b_minuendDone    = (l_minuendItem == (l_minuendItemCount - 1));
+            b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount
+                    - 1));
+        }
+
+        // check if the final polygon needs to be clipped
+        if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) {
+            // project onto subtrahend
+            double l_slope    = (l_y4 - l_y3) / (l_x4 - l_x3);
+            l_subtrahendNextX = l_minuendNextX;
+            l_subtrahendNextY = new Double((l_slope * l_x2)
+                    + (l_y3 - (l_slope * l_x3)));
+        }
+
+        if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) {
+            // project onto minuend
+            double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
+            l_minuendNextX = l_subtrahendNextX;
+            l_minuendNextY = new Double((l_slope * l_x4)
+                    + (l_y1 - (l_slope * l_x1)));
+        }
+
+        // consider last point of minuend and subtrahend for determining
+        // positivity
+        l_minuendMaxY    = Math.max(l_minuendMaxY,
+                l_minuendNextY.doubleValue());
+        l_subtrahendMaxY = Math.max(l_subtrahendMaxY,
+                l_subtrahendNextY.doubleValue());
+        l_minuendMinY    = Math.min(l_minuendMinY,
+                l_minuendNextY.doubleValue());
+        l_subtrahendMinY = Math.min(l_subtrahendMinY,
+                l_subtrahendNextY.doubleValue());
+
+        // add the last point of the minuned and subtrahend
+        l_minuendXs.add(l_minuendNextX);
+        l_minuendYs.add(l_minuendNextY);
+        l_subtrahendXs.add(l_subtrahendNextX);
+        l_subtrahendYs.add(l_subtrahendNextY);
+
+        // create the polygon
+        // add the minuend's points to polygon
+        l_polygonXs.addAll(l_minuendXs);
+        l_polygonYs.addAll(l_minuendYs);
+
+        // add the subtrahend's points to the polygon in reverse
+        Collections.reverse(l_subtrahendXs);
+        Collections.reverse(l_subtrahendYs);
+        l_polygonXs.addAll(l_subtrahendXs);
+        l_polygonYs.addAll(l_subtrahendYs);
+
+        // create an actual polygon
+        b_positive = (l_subtrahendMaxY <= l_minuendMaxY)
+                && (l_subtrahendMinY <= l_minuendMinY);
+        createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
+                x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
+    }
+
+    /**
+     * Draws the visual representation of a single data item, second pass.  In
+     * the second pass, the renderer draws the lines and shapes for the
+     * individual points in the two series.
+     *
+     * @param x_graphics  the graphics device.
+     * @param x_dataArea  the area within which the data is being drawn.
+     * @param x_info  collects information about the drawing.
+     * @param x_plot  the plot (can be used to obtain standard color
+     *         information etc).
+     * @param x_domainAxis  the domain (horizontal) axis.
+     * @param x_rangeAxis  the range (vertical) axis.
+     * @param x_dataset  the dataset.
+     * @param x_series  the series index (zero-based).
+     * @param x_item  the item index (zero-based).
+     * @param x_crosshairState  crosshair information for the plot
+     *                          (<code>null</code> permitted).
+     */
+    protected void drawItemPass1(Graphics2D x_graphics,
+                                 Rectangle2D x_dataArea,
+                                 PlotRenderingInfo x_info,
+                                 XYPlot x_plot,
+                                 ValueAxis x_domainAxis,
+                                 ValueAxis x_rangeAxis,
+                                 XYDataset x_dataset,
+                                 int x_series,
+                                 int x_item,
+                                 CrosshairState x_crosshairState) {
+
+        Shape l_entityArea = null;
+        EntityCollection l_entities = null;
+        if (null != x_info) {
+            l_entities = x_info.getOwner().getEntityCollection();
+        }
+
+        Paint l_seriesPaint   = getItemPaint(x_series, x_item);
+        Stroke l_seriesStroke = getItemStroke(x_series, x_item);
+        x_graphics.setPaint(l_seriesPaint);
+        x_graphics.setStroke(l_seriesStroke);
+
+        PlotOrientation l_orientation      = x_plot.getOrientation();
+        RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
+        RectangleEdge l_rangeAxisLocation  = x_plot.getRangeAxisEdge();
+
+        double l_x0 = x_dataset.getXValue(x_series, x_item);
+        double l_y0 = x_dataset.getYValue(x_series, x_item);
+        double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea,
+                l_domainAxisLocation);
+        double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea,
+                l_rangeAxisLocation);
+
+        if (getShapesVisible()) {
+            Shape l_shape = getItemShape(x_series, x_item);
+            if (l_orientation == PlotOrientation.HORIZONTAL) {
+                l_shape = ShapeUtilities.createTranslatedShape(l_shape,
+                        l_y1, l_x1);
+            }
+            else {
+                l_shape = ShapeUtilities.createTranslatedShape(l_shape,
+                        l_x1, l_y1);
+            }
+            if (l_shape.intersects(x_dataArea)) {
+                x_graphics.setPaint(getItemPaint(x_series, x_item));
+                x_graphics.fill(l_shape);
+            }
+            l_entityArea = l_shape;
+        }
+
+        // add an entity for the item...
+        if (null != l_entities) {
+            if (null == l_entityArea) {
+                l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2),
+                        4, 4);
+            }
+            String l_tip = null;
+            XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series,
+                    x_item);
+            if (null != l_tipGenerator) {
+                l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series,
+                        x_item);
+            }
+            String l_url = null;
+            XYURLGenerator l_urlGenerator = getURLGenerator();
+            if (null != l_urlGenerator) {
+                l_url = l_urlGenerator.generateURL(x_dataset, x_series,
+                        x_item);
+            }
+            XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset,
+                    x_series, x_item, l_tip, l_url);
+            l_entities.add(l_entity);
+        }
+
+        // draw the item label if there is one...
+        if (isItemLabelVisible(x_series, x_item)) {
+            drawItemLabel(x_graphics, l_orientation, x_dataset, x_series,
+                          x_item, l_x1, l_y1, (l_y1 < 0.0));
+        }
+
+        int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis);
+        int l_rangeAxisIndex  = x_plot.getRangeAxisIndex(x_rangeAxis);
+        updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex,
+                              l_rangeAxisIndex, l_x1, l_y1, l_orientation);
+
+        if (0 == x_item) {
+            return;
+        }
+
+        double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series,
+                (x_item - 1)), x_dataArea, l_domainAxisLocation);
+        double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series,
+                (x_item - 1)), x_dataArea, l_rangeAxisLocation);
+
+        Line2D l_line = null;
+        if (PlotOrientation.HORIZONTAL == l_orientation) {
+            l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2);
+        }
+        else if (PlotOrientation.VERTICAL == l_orientation) {
+            l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2);
+        }
+
+        if ((null != l_line) && l_line.intersects(x_dataArea)) {
+            x_graphics.setPaint(getItemPaint(x_series, x_item));
+            x_graphics.setStroke(getItemStroke(x_series, x_item));
+            x_graphics.draw(l_line);
+        }
+    }
+
+    /**
+     * Determines if a dataset is degenerate.  A degenerate dataset is a
+     * dataset where either series has less than two (2) points.
+     *
+     * @param x_dataset  the dataset.
+     * @param x_impliedZeroSubtrahend  if false, do not check the subtrahend
+     *
+     * @return true if the dataset is degenerate.
+     */
+    private boolean isEitherSeriesDegenerate(XYDataset x_dataset,
+            boolean x_impliedZeroSubtrahend) {
+
+        if (x_impliedZeroSubtrahend) {
+            return (x_dataset.getItemCount(0) < 2);
+        }
+
+        return ((x_dataset.getItemCount(0) < 2)
+                || (x_dataset.getItemCount(1) < 2));
+    }
+
+    /**
+     * Determines if the two (2) series are disjoint.
+     * Disjoint series do not overlap in the domain space.
+     *
+     * @param x_dataset  the dataset.
+     *
+     * @return true if the dataset is degenerate.
+     */
+    private boolean areSeriesDisjoint(XYDataset x_dataset) {
+
+        int l_minuendItemCount = x_dataset.getItemCount(0);
+        double l_minuendFirst  = x_dataset.getXValue(0, 0);
+        double l_minuendLast   = x_dataset.getXValue(0, l_minuendItemCount - 1);
+
+        int l_subtrahendItemCount = x_dataset.getItemCount(1);
+        double l_subtrahendFirst  = x_dataset.getXValue(1, 0);
+        double l_subtrahendLast   = x_dataset.getXValue(1,
+                l_subtrahendItemCount - 1);
+
+        return ((l_minuendLast < l_subtrahendFirst)
+                || (l_subtrahendLast < l_minuendFirst));
+    }
+
+    /**
+     * Draws the visual representation of a polygon
+     *
+     * @param x_graphics  the graphics device.
+     * @param x_dataArea  the area within which the data is being drawn.
+     * @param x_plot  the plot (can be used to obtain standard color
+     *                information etc).
+     * @param x_domainAxis  the domain (horizontal) axis.
+     * @param x_rangeAxis  the range (vertical) axis.
+     * @param x_positive  indicates if the polygon is positive (true) or
+     *                    negative (false).
+     * @param x_xValues  a linked list of the x values (expects values to be
+     *                   of type Double).
+     * @param x_yValues  a linked list of the y values (expects values to be
+     *                   of type Double).
+     */
+    private void createPolygon (Graphics2D x_graphics,
+                                Rectangle2D x_dataArea,
+                                XYPlot x_plot,
+                                ValueAxis x_domainAxis,
+                                ValueAxis x_rangeAxis,
+                                boolean x_positive,
+                                LinkedList x_xValues,
+                                LinkedList x_yValues) {
+
+        PlotOrientation l_orientation      = x_plot.getOrientation();
+        RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
+        RectangleEdge l_rangeAxisLocation  = x_plot.getRangeAxisEdge();
+
+        Object[] l_xValues = x_xValues.toArray();
+        Object[] l_yValues = x_yValues.toArray();
+
+        GeneralPath l_path = new GeneralPath();
+
+        if (PlotOrientation.VERTICAL == l_orientation) {
+            double l_x = x_domainAxis.valueToJava2D((
+                    (Double) l_xValues[0]).doubleValue(), x_dataArea,
+                    l_domainAxisLocation);
+            if (this.roundXCoordinates) {
+                l_x = Math.rint(l_x);
+            }
+
+            double l_y = x_rangeAxis.valueToJava2D((
+                    (Double) l_yValues[0]).doubleValue(), x_dataArea,
+                    l_rangeAxisLocation);
+
+            l_path.moveTo((float) l_x, (float) l_y);
+            for (int i = 1; i < l_xValues.length; i++) {
+                l_x = x_domainAxis.valueToJava2D((
+                        (Double) l_xValues[i]).doubleValue(), x_dataArea,
+                        l_domainAxisLocation);
+                if (this.roundXCoordinates) {
+                    l_x = Math.rint(l_x);
+                }
+
+                l_y = x_rangeAxis.valueToJava2D((
+                        (Double) l_yValues[i]).doubleValue(), x_dataArea,
+                        l_rangeAxisLocation);
+                l_path.lineTo((float) l_x, (float) l_y);
+            }
+            l_path.closePath();
+        }
+        else {
+            double l_x = x_domainAxis.valueToJava2D((
+                    (Double) l_xValues[0]).doubleValue(), x_dataArea,
+                    l_domainAxisLocation);
+            if (this.roundXCoordinates) {
+                l_x = Math.rint(l_x);
+            }
+
+            double l_y = x_rangeAxis.valueToJava2D((
+                    (Double) l_yValues[0]).doubleValue(), x_dataArea,
+                    l_rangeAxisLocation);
+
+            l_path.moveTo((float) l_y, (float) l_x);
+            for (int i = 1; i < l_xValues.length; i++) {
+                l_x = x_domainAxis.valueToJava2D((
+                        (Double) l_xValues[i]).doubleValue(), x_dataArea,
+                        l_domainAxisLocation);
+                if (this.roundXCoordinates) {
+                    l_x = Math.rint(l_x);
+                }
+
+                l_y = x_rangeAxis.valueToJava2D((
+                        (Double) l_yValues[i]).doubleValue(), x_dataArea,
+                        l_rangeAxisLocation);
+                l_path.lineTo((float) l_y, (float) l_x);
+            }
+            l_path.closePath();
+        }
+
+        if (l_path.intersects(x_dataArea)) {
+            x_graphics.setPaint(x_positive ? getPositivePaint()
+                    : getNegativePaint());
+            x_graphics.fill(l_path);
+        }
+    }
+
+    /**
+     * Returns a default legend item for the specified series.  Subclasses
+     * should override this method to generate customised items.
+     *
+     * @param datasetIndex  the dataset index (zero-based).
+     * @param series  the series index (zero-based).
+     *
+     * @return A legend item for the series.
+     */
+    public LegendItem getLegendItem(int datasetIndex, int series) {
+        LegendItem result = null;
+        XYPlot p = getPlot();
+        if (p != null) {
+            XYDataset dataset = p.getDataset(datasetIndex);
+            if (dataset != null) {
+                if (getItemVisible(series, 0)) {
+                    String label = getLegendItemLabelGenerator().generateLabel(
+                            dataset, series);
+                    String description = label;
+                    String toolTipText = null;
+                    if (getLegendItemToolTipGenerator() != null) {
+                        toolTipText
+                            = getLegendItemToolTipGenerator().generateLabel(
+                                    dataset, series);
+                    }
+                    String urlText = null;
+                    if (getLegendItemURLGenerator() != null) {
+                        urlText = getLegendItemURLGenerator().generateLabel(
+                                dataset, series);
+                    }
+                    Paint paint = lookupSeriesPaint(series);
+                    Stroke stroke = lookupSeriesStroke(series);
+                    Shape line = getLegendLine();
+                    result = new LegendItem(label, description,
+                            toolTipText, urlText, line, stroke, paint);
+                    result.setLabelFont(lookupLegendTextFont(series));
+                    Paint labelPaint = lookupLegendTextPaint(series);
+                    if (labelPaint != null) {
+                        result.setLabelPaint(labelPaint);
+                    }
+                    result.setDataset(dataset);
+                    result.setDatasetIndex(datasetIndex);
+                    result.setSeriesKey(dataset.getSeriesKey(series));
+                    result.setSeriesIndex(series);
+                }
+            }
+
+        }
+
+        return result;
+
+    }
+
+    /**
+     * Tests this renderer for equality with an arbitrary object.
+     *
+     * @param obj  the object (<code>null</code> permitted).
+     *
+     * @return A boolean.
+     */
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof StableXYDifferenceRenderer)) {
+            return false;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj;
+        if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) {
+            return false;
+        }
+        if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) {
+            return false;
+        }
+        if (this.shapesVisible != that.shapesVisible) {
+            return false;
+        }
+        if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
+            return false;
+        }
+        if (this.roundXCoordinates != that.roundXCoordinates) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns a clone of the renderer.
+     *
+     * @return A clone.
+     *
+     * @throws CloneNotSupportedException  if the renderer cannot be cloned.
+     */
+    public Object clone() throws CloneNotSupportedException {
+        StableXYDifferenceRenderer clone = (StableXYDifferenceRenderer) super.clone();
+        clone.legendLine = ShapeUtilities.clone(this.legendLine);
+        return clone;
+    }
+
+    /**
+     * Provides serialization support.
+     *
+     * @param stream  the output stream.
+     *
+     * @throws IOException  if there is an I/O error.
+     */
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        stream.defaultWriteObject();
+        SerialUtilities.writePaint(this.positivePaint, stream);
+        SerialUtilities.writePaint(this.negativePaint, stream);
+        SerialUtilities.writeShape(this.legendLine, stream);
+    }
+
+    /**
+     * Provides serialization support.
+     *
+     * @param stream  the input stream.
+     *
+     * @throws IOException  if there is an I/O error.
+     * @throws ClassNotFoundException  if there is a classpath problem.
+     */
+    private void readObject(ObjectInputStream stream)
+        throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+        this.positivePaint = SerialUtilities.readPaint(stream);
+        this.negativePaint = SerialUtilities.readPaint(stream);
+        this.legendLine = SerialUtilities.readShape(stream);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,358 @@
+package de.intevation.flys.jfree;
+
+import org.apache.log4j.Logger;
+
+import java.util.Iterator;
+
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.Line2D;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.BasicStroke;
+
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.util.LineUtilities;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.entity.XYAnnotationEntity;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.plot.Plot;
+
+import org.jfree.data.Range;
+
+import org.jfree.text.TextUtilities;
+
+import org.jfree.ui.RectangleEdge;
+import org.jfree.ui.TextAnchor;
+
+import de.intevation.flys.utils.ThemeAccess;
+
+/**
+ * Custom Annotations class that is drawn only if no collisions with other
+ * already drawn CustomAnnotations in current plot are found. A mark on
+ * the axis is shown in all cases, though.
+ * Draws a given text and a line to it from either axis.
+ */
+public class StickyAxisAnnotation extends XYTextAnnotation {
+
+    /** Logger for this class. */    
+    private static Logger logger =
+        Logger.getLogger(StickyAxisAnnotation.class);
+
+    /** Simplified view on axes. */
+    public static enum SimpleAxis {
+        X_AXIS, /** Usually "horizontal". */
+        Y_AXIS  /** Usually "vertical". */
+    }
+
+    /** Which axis to stick to. */
+    protected SimpleAxis stickyAxis = SimpleAxis.X_AXIS;
+
+    /** The 1-dimensional position of this annotation. */
+    protected float pos;
+
+    /** Theme attributes. */
+    protected Color textColor;
+    protected Color lineColor;
+    protected Font font;
+    protected int lineWidth;
+    protected String textOrientation = "vertical";
+    protected Color textBackground;
+    protected boolean showBackground;
+
+
+    /**
+     * Trivial constructor.
+     *
+     * @param text Text to display.
+     * @param x    X-position in dataspace (typical horizontal, in km).
+     * @param y    Y-position in dataspace (typical vertical, in m).
+     * @deprecated
+     */
+    public StickyAxisAnnotation(String text, float x, float y) {
+        super(text, x, y);
+        setStickyAxis(SimpleAxis.X_AXIS);
+    }
+
+
+    /**
+     * Constructor with implicit sticky x-axis.
+     * @param text       the text to display.
+     * @param pos        the position at which to draw the text and mark.
+     */
+    public StickyAxisAnnotation(String text, float pos) {
+        this(text, pos, pos, SimpleAxis.X_AXIS);
+    }
+
+
+    /**
+     * Constructor with given explicit axis.
+     * @param text       the text to display.
+     * @param pos        the position at which to draw the text and mark.
+     * @param stickyAxis the axis at which to stick (and to which 'pos' is
+     *                   relative).
+     */
+    public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis) {
+        super(text, pos, pos);
+        setStickyAxis(stickAxis);
+        this.pos = pos;
+    }
+
+    /**
+     * Legacy-Constructor.
+     * @deprecated
+     */
+    public StickyAxisAnnotation(String text, float x, float y,
+            SimpleAxis stickAxis) {
+        super(text, x, y);
+        setStickyAxis(stickAxis);
+        this.pos = x;
+    }
+
+
+    /**
+     * Sets the "sticky axis" (whether to draw annotations at the
+     * X- or the Y-Axis.
+     *
+     * @param stickyAxis axis to stick to.
+     */
+    public void setStickyAxis(SimpleAxis stickyAxis) {
+        this.stickyAxis = stickyAxis;
+        if (stickyAxis == SimpleAxis.X_AXIS){
+            float o = 270f;
+            if(textOrientation.equals("horizontal")) {
+                o = 0f;
+            }
+            this.setRotationAngle(o * (Math.PI / 180f));
+            this.setRotationAnchor(TextAnchor.CENTER_LEFT);
+            this.setTextAnchor(TextAnchor.CENTER_LEFT);
+        } else {
+            this.setRotationAngle(0f * (Math.PI / 180f));
+            this.setRotationAnchor(TextAnchor.CENTER_LEFT);
+            this.setTextAnchor(TextAnchor.CENTER_LEFT);
+        }
+    }
+
+
+    /**
+     * Draws a small line at axis where this annotation resides.
+     *
+     * @param g2          the graphics device.
+     * @param dataArea    the data area.
+     * @param domainAxis  the domain axis.
+     * @param rangeAxis   the range axis.
+     * @param domainEdge  the domain edge.
+     * @param rangeEdge   the range edge.
+     * @param orientation the plot orientation.
+     */
+    protected void drawAxisMark(
+            java.awt.Graphics2D g2,
+            java.awt.geom.Rectangle2D dataArea,
+            ValueAxis domainAxis,
+            ValueAxis rangeAxis,
+            RectangleEdge domainEdge,
+            RectangleEdge rangeEdge,
+            PlotOrientation orientation) {
+        float j2DX1 = 0.0f;
+        float j2DX2 = 0.0f;
+        float j2DY1 = 0.0f;
+        float j2DY2 = 0.0f;
+        float x = (float) getX();
+        float y = (float) getY();
+        /* When dependent on X/Y-Axis and orientation, following
+           can be used as a base:
+        if (orientation == PlotOrientation.VERTICAL) {
+            j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea,
+                        domainEdge);
+            j2DY1 = (float) rangeAxis.valueToJava2D(y, dataArea,
+                        rangeEdge);
+            j2DX2 = (float) domainAxis.valueToJava2D(x, dataArea,
+                        domainEdge);
+            j2DY2 = (float) rangeAxis.valueToJava2D(y, dataArea,
+                        rangeEdge);
+            }
+        else if (orientation == PlotOrientation.HORIZONTAL) {
+            j2DY1 = (float) domainAxis.valueToJava2D(x, dataArea,
+                    domainEdge);
+            j2DX1 = (float) rangeAxis.valueToJava2D(y, dataArea,
+                    rangeEdge);
+            j2DY2 = (float) domainAxis.valueToJava2D(x, dataArea,
+                    domainEdge);
+            j2DX2 = (float) rangeAxis.valueToJava2D(y, dataArea,
+                    rangeEdge);
+        }
+
+        g2.setPaint(this.paint);
+        g2.setStroke(this.stroke);
+        */
+        if (this.stickyAxis == SimpleAxis.X_AXIS) {
+            j2DY1 = (float) RectangleEdge.coordinate(dataArea, domainEdge);
+            double rangeLow = rangeAxis.getRange().getLowerBound();
+            // Line ends at 1.5% of full distance.
+            j2DY2 = (float) rangeAxis.valueToJava2D(
+                      (1f - 0.015f) * rangeLow + 0.015f * 
+                      rangeAxis.getRange().getUpperBound(),
+                     dataArea, rangeEdge);
+            j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, domainEdge);
+            j2DX2 = j2DX1;
+        } else {
+            j2DX1 = (float) RectangleEdge.coordinate(dataArea, rangeEdge);
+            Range domainRange = domainAxis.getRange();
+            double rangeLow = domainRange.getLowerBound();
+            // Line ends at 1.5% of full distance.
+            j2DX2 = (float) domainAxis.valueToJava2D(
+                      (1f - 0.015f) * rangeLow + 0.015f * 
+                      domainRange.getUpperBound(),
+                     dataArea, domainEdge);
+            j2DY1 = (float) rangeAxis.valueToJava2D(pos, dataArea, rangeEdge);
+            j2DY2 = j2DY1;
+        }
+
+        Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2);
+
+        // line is clipped to avoid JRE bug 6574155, for more info
+        // see JFreeChart bug 2221495
+        boolean visible = LineUtilities.clipLine(line, dataArea);
+        if (visible) {
+            setOutlineStroke(new BasicStroke((float) lineWidth));
+            g2.setStroke(getOutlineStroke());
+            g2.setPaint(lineColor);
+            g2.draw(line);
+        }
+    }
+
+
+    /**
+     * Draw the Annotiation if it does not collide with other already drawn
+     * Annotations.
+     *
+     * @param g2            the graphics device.
+     * @param plot          the plot.
+     * @param dataArea      the data area.
+     * @param domainAxis    the domain axis.
+     * @param rangeAxis     the range axis.
+     * @param rendererIndex the render index.
+     * @param info          state information, escpecially collects info about
+     *                      already drawn shapes (and thus annotations), used
+     *                      for collision detection.
+     */
+    @Override
+    public void draw(
+       java.awt.Graphics2D g2,
+       XYPlot plot,
+       java.awt.geom.Rectangle2D dataArea,
+       ValueAxis domainAxis,
+       ValueAxis rangeAxis,
+       int rendererIndex,
+       PlotRenderingInfo info) {
+
+        if (info == null)
+            return;
+
+        if (domainAxis == null || rangeAxis == null
+                || domainAxis.getRange()    == null
+                || rangeAxis.getRange()     == null
+                ) {
+            logger.error("Annotation cannot be drawn (missing axis).");
+            return;
+        }
+
+        // Calculate the bounding box.
+        ChartRenderingInfo chartInfo = info.getOwner();
+
+        PlotOrientation orientation = plot.getOrientation();
+        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
+            plot.getDomainAxisLocation(), orientation);
+        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
+            plot.getRangeAxisLocation(), orientation);
+        float anchorX = 0f;
+        float anchorY = 0.0f;
+        if (this.stickyAxis == SimpleAxis.X_AXIS) {
+            // Text starts at 1.5% of full distance.
+            float rangeLow = (float) rangeAxis.getRange().getLowerBound();
+            float y = rangeLow + 0.02f * ((float)
+                    rangeAxis.getRange().getUpperBound() - rangeLow);
+            setY(y);
+
+            anchorX = (float) domainAxis.valueToJava2D(
+                getX(), dataArea, domainEdge);
+            anchorY = (float) rangeAxis.valueToJava2D(
+                getY(), dataArea, rangeEdge);
+        } else {
+            float rangeLow = (float) domainAxis.getRange().getLowerBound();
+            float x = rangeLow + 0.02f * ((float)
+                    domainAxis.getRange().getUpperBound() - rangeLow);
+            setX(x);
+            anchorX = (float) domainAxis.valueToJava2D(
+                getX(), dataArea, domainEdge);
+            anchorY = (float) rangeAxis.valueToJava2D(
+                getY(), dataArea, rangeEdge);
+        }
+        if (orientation == PlotOrientation.HORIZONTAL) {
+            float tempAnchor = anchorX;
+            anchorX = anchorY;
+            anchorY = tempAnchor;
+        }
+
+        //Call to apply orientation.
+        setStickyAxis(stickyAxis);
+
+        // Always draw the small line at axis.
+        drawAxisMark(g2, dataArea, domainAxis, rangeAxis, domainEdge,
+                rangeEdge, orientation);
+
+        Shape hotspot = TextUtilities.calculateRotatedStringBounds(
+           getText(), g2, anchorX, anchorY, getTextAnchor(),
+           getRotationAngle(), getRotationAnchor());
+        Rectangle2D hotspotBox = hotspot.getBounds2D();
+        // Check for collisions with other XYAnnotations.
+        for (Iterator i = chartInfo.getEntityCollection().iterator();
+                i.hasNext(); ) {
+            Object next = i.next();
+            // Collision with other stuff than XYAnnotations are okay.
+            if (next instanceof XYAnnotationEntity) {
+                XYAnnotationEntity drawnShape = (XYAnnotationEntity) next;
+                if (drawnShape.getArea().intersects(hotspotBox)) {
+                    // Found collision, early stop.
+                    return;
+                }
+            }
+        }
+
+        // Draw the background.
+        if (showBackground) {
+            g2.setStroke(new BasicStroke ((float) 1));
+            g2.setBackground(textBackground);
+            g2.setPaint(textBackground);
+            hotspot = TextUtilities.calculateRotatedStringBounds(
+                getText(), g2, anchorX, anchorY, getTextAnchor(),
+                getRotationAngle(), getRotationAnchor());
+            g2.fill(hotspot);
+            g2.draw(hotspot);
+        }
+
+        // Draw the text.
+        g2.setPaint(textColor);
+        g2.setFont(font);
+        TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY,
+                getTextAnchor(), getRotationAngle(), getRotationAnchor());
+
+        // Add info that we have drawn this Annotation.
+        addEntity(info, hotspot, rendererIndex, getToolTipText(), getURL());
+    }
+
+
+    public void applyTheme(ThemeAccess ta) {
+        lineWidth       = ta.parseLineWidth();
+        lineColor       = ta.parseLineColorField();
+        textColor       = ta.parseTextColor();
+        font            = ta.parseTextFont();
+        textOrientation = ta.parseTextOrientation();
+        textBackground  = ta.parseTextBackground();
+        showBackground  = ta.parseShowTextBackground();
+    }
+}
+// vim: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/jfree/XYDatasetToZeroMapper.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,135 @@
+package de.intevation.flys.jfree;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.jfree.data.xy.XYDataset;
+
+import org.jfree.data.general.DatasetChangeListener;
+import org.jfree.data.general.DatasetGroup;
+
+import org.jfree.data.DomainOrder;
+
+import java.io.Serializable;
+
+public class XYDatasetToZeroMapper
+implements   Serializable
+{
+    protected int seriesA;
+    protected int seriesB;
+
+    public class MappedXYDataset implements XYDataset, Serializable {
+
+        protected XYDataset parent;
+
+        public MappedXYDataset() {
+        }
+
+        public MappedXYDataset(XYDataset parent) {
+            this.parent = parent;
+        }
+
+        protected int remap(int series) {
+            switch (series) {
+                case 0: return seriesA;
+                case 1: return seriesB;
+            }
+            return series;
+        }
+
+        @Override
+        public int getSeriesCount() {
+            return seriesA != seriesB ? 2 : 1;
+        }
+
+        @Override
+        public Comparable getSeriesKey(int series) {
+            return parent.getSeriesKey(remap(series));
+        }
+
+        @Override
+        public int indexOf(Comparable seriesKey) {
+            Comparable cmpA = parent.getSeriesKey(seriesA);
+            if (cmpA == seriesKey
+            || (cmpA != null && cmpA.equals(seriesKey))) {
+                return 0;
+            }
+            Comparable cmpB = parent.getSeriesKey(seriesB);
+            if (cmpB == seriesKey
+            || (cmpB != null && cmpB.equals(seriesKey))) {
+                return 1;
+            }
+            return -1;
+        }
+
+        @Override
+        public void addChangeListener(DatasetChangeListener listener) {
+            parent.addChangeListener(listener);
+        }
+
+        @Override
+        public void removeChangeListener(DatasetChangeListener listener) {
+            parent.removeChangeListener(listener);
+        }
+
+        @Override
+        public DatasetGroup getGroup() {
+            return parent.getGroup();
+        }
+
+        @Override
+        public void setGroup(DatasetGroup group) {
+            parent.setGroup(group);
+        }
+
+        @Override
+        public DomainOrder getDomainOrder() {
+            return parent.getDomainOrder();
+        }
+
+        @Override
+        public int getItemCount(int series) {
+            return parent.getItemCount(remap(series));
+        }
+
+        @Override
+        public Number getX(int series, int item) {
+            return parent.getX(remap(series), item);
+        }
+
+        @Override
+        public double getXValue(int series, int item) {
+            return parent.getXValue(remap(series), item);
+        }
+
+        @Override
+        public Number getY(int series, int item) {
+            return parent.getY(remap(series), item);
+        }
+
+        @Override
+        public double getYValue(int series, int item) {
+            return parent.getYValue(remap(series), item);
+        }
+    } // class MappedXYDataset
+
+    public XYDatasetToZeroMapper() {
+    }
+
+    public XYDatasetToZeroMapper(int seriesA) {
+        this(seriesA, seriesA);
+    }
+
+    public XYDatasetToZeroMapper(int seriesA, int seriesB) {
+        this.seriesA = seriesA;
+        this.seriesB = seriesB;
+    }
+
+    public Iterator<XYDataset> iterator(XYDataset dataset) {
+        List<XYDataset> list = new ArrayList<XYDataset>(1);
+        list.add(new MappedXYDataset(dataset));
+        return list.iterator();
+    }
+}
+// vim: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:51 2012 +0200
@@ -0,0 +1,193 @@
+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;
+
+    protected String facet;
+
+    protected int index;
+
+
+    /** 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 String getFacet() {
+        return facet;
+    }
+
+
+    public void setFacet(String facet) {
+        this.facet = facet;
+    }
+
+
+    public int getIndex() {
+        return index;
+    }
+
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+
+    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");
+        theme.setAttribute("facet", facet);
+        theme.setAttribute("index", String.valueOf(index));
+
+        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:51 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:51 2012 +0200
@@ -0,0 +1,119 @@
+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();
+
+
+    String getFacet();
+
+    void setFacet(String facet);
+
+    int getIndex();
+
+    void setIndex(int index);
+
+
+    /**
+     * 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/ThemeAccess.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,96 @@
+package de.intevation.flys.utils;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import org.w3c.dom.Document;
+
+
+public class ThemeAccess
+{
+    protected Document theme;
+
+    protected Integer lineWidth;
+
+    protected Color   lineColor;
+    protected Color   textColor;
+    protected Font    font;
+    protected String  textOrientation;
+    protected Color   textBackground;
+    protected Boolean showTextBackground;
+
+    public ThemeAccess(Document theme) {
+        this.theme = theme;
+    }
+
+    public int parseLineWidth() {
+        if (lineWidth == null) {
+            lineWidth = ThemeUtil.parseLineWidth(theme);
+        }
+        return lineWidth;
+    }
+
+    public Color parseLineColorField() {
+        if (lineColor == null) {
+            lineColor = ThemeUtil.parseLineColorField(theme);
+            if (lineColor == null) {
+                lineColor = Color.BLACK;
+            }
+        }
+        return lineColor;
+    }
+
+    public Color parseTextColor() {
+        if (textColor == null) {
+            textColor = ThemeUtil.parseTextColor(theme);
+            if (textColor == null) {
+                textColor = Color.BLACK;
+            }
+        }
+        return textColor;
+    }
+
+    public Font parseTextFont() {
+        if (font == null) {
+            font = ThemeUtil.parseTextFont(theme);
+            if (font == null) {
+                font = new Font("Arial", Font.BOLD, 10);
+            }
+        }
+        return font;
+    }
+
+    public String parseTextOrientation() {
+        if (textOrientation == null) {
+            textOrientation = ThemeUtil.parseTextOrientation(theme);
+            if (textOrientation == null) {
+                textOrientation = "horizontal";
+            }
+        }
+        return textOrientation;
+    }
+
+
+    public Color parseTextBackground() {
+        if (textBackground == null) {
+            textBackground = ThemeUtil.parseTextBackground(theme);
+            if (textBackground == null) {
+                textBackground = Color.WHITE;
+            }
+        }
+        return textBackground;
+    }
+
+    public boolean parseShowTextBackground() {
+        if (showTextBackground == null) {
+            showTextBackground = ThemeUtil.parseShowTextBackground(theme);
+        }
+        return showTextBackground;
+    }
+
+    /**
+    more of 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/themes/ThemeFactory.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,253 @@
+package de.intevation.flys.themes;
+
+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.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;
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ *
+ * Mapping-matching rules:
+ *
+ */
+public class ThemeFactory {
+
+    private static Logger logger = Logger.getLogger(ThemeFactory.class);
+
+    /** Trivial, hidden constructor. */
+    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;
+    }
+
+
+    /**
+     * Get first matching theme for facet.
+     *
+     * @param name    Name to match "from" of theme mapping.
+     * @param pattern String to 'compare' to pattern in mapping.
+     * @param output  Name of the current output
+     *
+     * @return First matching theme.
+     */
+    public static Theme getTheme(
+        FLYSContext c,
+        String name,
+        String pattern,
+        String output)
+    {
+        logger.debug("Search theme for: " + name + " - pattern: " + pattern);
+
+        if (c == null || name == null) {
+            logger.warn("Cannot search for theme.");
+            return null;
+        }
+
+        // Fetch mapping and themes.
+        Map<String, List<ThemeMapping>> map = (Map<String, List<ThemeMapping>>)
+            c.get(FLYSContext.THEME_MAPPING);
+
+        Map<String, Theme> t = (Map<String, Theme>)
+            c.get(FLYSContext.THEMES);
+
+        FLYSArtifact artifact = (FLYSArtifact) c.get(FLYSContext.ARTIFACT_KEY);
+
+        if (map == null || map.size() == 0 || t == null || t.size() == 0) {
+            logger.warn("No mappings or themes found. Cannot retrieve theme!");
+            return null;
+        }
+
+        List<ThemeMapping> mapping = map.get(name);
+
+        if (mapping == null) {
+            logger.warn("No theme found for mapping: " + name);
+            return null;
+        }
+
+        // Take first mapping of which all conditions are satisfied.
+        for (ThemeMapping tm: mapping) {
+            if (name.equals(tm.getFrom())
+                && tm.applyPattern(pattern)
+                && tm.masterAttrMatches(artifact)
+                && tm.outputMatches(output))
+            {
+                return t.get(tm.getTo());
+            }
+        }
+
+        String msg =
+            "No theme found for '" + name +
+            "' with pattern '" + pattern + "' and output " + output + ".";
+
+        logger.warn(msg);
+
+        return null;
+    }
+
+
+    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:51 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/themes/ThemeMapping.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,135 @@
+package de.intevation.flys.themes;
+
+import org.apache.log4j.Logger;
+
+import java.io.Serializable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+/**
+ * Represents mapping to a theme (including conditions).
+ */
+public class ThemeMapping implements Serializable {
+
+    /** The logger that is used in this class */
+    private static Logger logger = Logger.getLogger(ThemeMapping.class);
+
+    /** Name from which to map. */
+    protected String from;
+
+    /** Name to which to map. */
+    protected String to;
+
+    /** Given pattern (held against facet description). */
+    protected String patternStr;
+
+    /** Given masterAttr pattern (held against masterartifacts attributes). */
+    protected String masterAttr;
+
+    /** Given output for which mapping is valid. */
+    protected String output;
+
+    protected Pattern pattern;
+
+
+    public ThemeMapping(String from, String to) {
+        this(from, to, null, null, null);
+    }
+
+
+    public ThemeMapping(
+        String from,
+        String to,
+        String patternStr,
+        String masterAttr,
+        String output)
+   {
+        this.from       = from;
+        this.to         = to;
+        this.patternStr = patternStr;
+        this.masterAttr = masterAttr;
+        this.output     = output;
+
+        this.pattern = Pattern.compile(patternStr);
+    }
+
+
+    public String getFrom() {
+        return from;
+    }
+
+
+    /**
+     * Get name of theme that is mapped to.
+     */
+    public String getTo() {
+        return to;
+    }
+
+
+    /**
+     * Get pattern.
+     */
+    public String getPatternStr() {
+        return patternStr;
+    }
+
+
+    /**
+     * Match regular expression against text.
+     *
+     * @param text string to be matched against.
+     * @return true if pattern matches text or pattern is empty.
+     */
+    public boolean applyPattern(String text) {
+        if (patternStr == null || patternStr.equals("")) {
+            return true;
+        }
+        Matcher m = pattern.matcher(text);
+        return m.matches();
+    }
+
+
+    /**
+     * Inspects Artifacts data given the masterAttr-condition.
+     *
+     * The only condition implemented so far is 'key==value', for which
+     * the Artifacts data with name "key" has to be of value "value" in order
+     * for true to be returned.
+     *
+     * @param artifact Artifact of which to inspect data.
+     * @return true if no condition is specified or condition is met.
+     */
+    public boolean masterAttrMatches(FLYSArtifact artifact) {
+        if (masterAttr == null || masterAttr.equals("")) {
+           return true;
+        }
+
+        // Operator split.
+        String[] parts = masterAttr.split("==");
+        if (parts.length != 2) {
+            logger.error("ThemeMapping could not parse masterAttr.-condition:_"
+                + masterAttr + "_");
+            return false;
+        }
+
+        // Test.
+        return artifact.getDataAsString(parts[0]).equals(parts[1]);
+    }
+
+
+    /**
+     * Returns true if no output condition exists, or the condition is met
+     * in parameter output.
+     */
+    public boolean outputMatches(String output) {
+        if (this.output == null || this.output.equals("")) {
+            return true;
+        }
+
+        return this.output.equals(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/utils/DataUtil.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,33 @@
+package de.intevation.flys.utils;
+
+import java.util.Random;
+
+import gnu.trove.TDoubleArrayList;
+
+public class DataUtil
+{
+    public static boolean guessWaterIncreasing(TDoubleArrayList data) {
+        return guessWaterIncreasing(data, 0.05f);
+    }
+
+    public static boolean guessWaterIncreasing(TDoubleArrayList data, float factor) {
+        int N = data.size();
+        if (N < 2) return false;
+    
+        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   = data.getQuick(pos1);
+            double w2   = data.getQuick(pos2);
+            if (w2 > w1) ++up;
+        }
+    
+        return up > samples/2;
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,136 @@
+package de.intevation.flys.utils;
+
+import java.util.Arrays;
+
+import de.intevation.flys.artifacts.math.Linear;
+
+
+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;
+        }
+
+        double max = Math.max(from, to);
+
+        for (int idx = 0; idx < num; idx++) {
+            if (lower > max) {
+                return Arrays.copyOfRange(values, 0, idx);
+            }
+
+            values[idx] = round(lower, precision);
+            lower      += step;
+        }
+
+        return values;
+    }
+
+    public static final double interpolateSorted(
+        double [] xs,
+        double [] ys,
+        double x
+    ) {
+        int lo = 0, hi = xs.length-1;
+
+        int mid = -1;
+
+        while (lo <= hi) {
+            mid = (lo + hi) >> 1;
+            double mx = xs[mid];
+                 if (x < mx) hi = mid - 1;
+            else if (x > mx) lo = mid + 1;
+            else return ys[mid];
+        }
+        if (mid < lo) {
+            return lo >= xs.length
+                ? Double.NaN
+                : Linear.linear(x, xs[mid], xs[mid+1], ys[mid], ys[mid+1]);
+        }
+        return hi < 0
+            ? Double.NaN
+            : Linear.linear(x, xs[mid-1], xs[mid], ys[mid-1], ys[mid]);
+    }
+
+    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/utils/FLYSUtils.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,368 @@
+package de.intevation.flys.utils;
+
+import org.apache.log4j.Logger;
+
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+
+import gnu.trove.TDoubleArrayList;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.MainValue;
+import de.intevation.flys.model.River;
+
+public class FLYSUtils {
+
+    /** The logger that is used in this utility. */
+    private static Logger logger = Logger.getLogger(FLYSUtils.class);
+
+    public static enum KM_MODE { RANGE, LOCATIONS, NONE };
+
+    public static final String XPATH_RIVER_PROJECTION =
+        "/artifact-database/floodmap/river[@name=$name]/srid/@value";
+
+    public static final String XPATH_SHAPEFILE_DIR =
+        "/artifact-database/floodmap/shapefile-path/@value";
+
+    public static final String XPATH_VELOCITY_LOGFILE =
+        "/artifact-database/floodmap/velocity/logfile/@path";
+
+    public static final String XPATH_MAPSERVER_URL =
+        "/artifact-database/floodmap/mapserver/server/@path";
+
+    public static final String XPATH_MAPFILE_PATH =
+        "/artifact-database/floodmap/mapserver/mapfile/@path";
+
+    public static final String XPATH_MAPFILE_TEMPLATE =
+        "/artifact-database/floodmap/mapserver/map-template/@path";
+
+    public static final String XPATH_MAPSERVER_TEMPLATE_PATH =
+        "/artifact-database/floodmap/mapserver/templates/@path";
+
+
+    private FLYSUtils() {
+    }
+
+
+    public static FLYSArtifact getArtifact(String uuid, CallContext context) {
+        try {
+            Artifact artifact = context.getDatabase().getRawArtifact(uuid);
+
+            if (artifact == null) {
+                logger.error("Artifact '" + uuid + "' does not exist.");
+                return null;
+            }
+
+            if (!(artifact instanceof FLYSArtifact)) {
+                logger.error("Artifact '" +uuid+ "' is no valid FLYSArtifact.");
+                return null;
+            }
+
+            return (FLYSArtifact) artifact;
+        }
+        // TODO: catch more selective
+        catch (Exception e) {
+            logger.error("Cannot get FLYSArtifact " + uuid + " from database (" + e.getMessage() + ").");
+            return null;
+        }
+    }
+
+
+    /**
+     * Returns the FLYSContext from context object.
+     *
+     * @param context The CallContext or the FLYSContext.
+     *
+     * @return the FLYSContext.
+     */
+    public static FLYSContext getFlysContext(Object context) {
+        return context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) ((CallContext) context).globalContext();
+    }
+
+
+    /**
+     * Convinience function to retrieve an XPath as string with replaced config
+     * directory.
+     *
+     * @param xpath The XPath expression.
+     *
+     * @return a string with replaced config directory.
+     */
+    public static String getXPathString(String xpath) {
+        String tmp = Config.getStringXPath(xpath);
+        tmp        = Config.replaceConfigDir(tmp);
+
+        return tmp;
+    }
+
+
+    public static KM_MODE getKmRangeMode(FLYSArtifact flys) {
+        String mode = flys.getDataAsString("ld_mode");
+
+        if (mode == null || mode.length() == 0) {
+            return KM_MODE.NONE;
+        }
+        else if (mode.equals("distance"))  {
+            return KM_MODE.RANGE;
+        }
+        else if (mode.equals("locations")) {
+            return KM_MODE.LOCATIONS;
+        }
+        else {
+            return KM_MODE.NONE;
+        }
+    }
+
+
+    public static double[] getKmRange(FLYSArtifact flys) {
+        switch (getKmRangeMode(flys)) {
+            case RANGE: {
+                return getKmFromTo(flys);
+            }
+
+            case LOCATIONS: {
+                double[] locs = getLocations(flys);
+                return new double[] { locs[0], locs[locs.length-1] };
+            }
+
+            case NONE: {
+                double[] locs = getLocations(flys);
+                if (locs != null) {
+                    return new double[] { locs[0], locs[locs.length-1] };
+                }
+                else {
+                    return getKmFromTo(flys);
+                }
+            }
+        }
+
+        return new double[] { Double.NaN, Double.NaN };
+    }
+
+
+    public static double[] getKmFromTo(FLYSArtifact flys) {
+        String strFrom = flys.getDataAsString("ld_from");
+        String strTo   = flys.getDataAsString("ld_to");
+
+        if (strFrom == null || strTo == null) {
+            return null;
+        }
+
+        try {
+            return new double[] {
+                Double.parseDouble(strFrom),
+                Double.parseDouble(strTo) };
+        }
+        catch (NumberFormatException nfe) {
+            return null;
+        }
+    }
+
+
+    public static double[] getLocations(FLYSArtifact flys) {
+        String locationStr = flys.getDataAsString("ld_locations");
+
+        if (locationStr == null || locationStr.length() == 0) {
+            return null;
+        }
+
+        String[] tmp               = locationStr.split(" ");
+        TDoubleArrayList locations = new TDoubleArrayList();
+
+        for (String l: tmp) {
+            try {
+                locations.add(Double.parseDouble(l));
+            }
+            catch (NumberFormatException nfe) {
+            }
+        }
+
+        locations.sort();
+
+        return locations.toNativeArray();
+    }
+
+
+    /**
+     * 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 static River getRiver(FLYSArtifact flys) {
+        String sRiver = flys.getDataAsString("river");
+
+        return (sRiver != null)
+            ? RiverFactory.getRiver(sRiver)
+            : null;
+    }
+
+
+    /**
+     * Extracts the SRID defined in the global configuration for the river
+     * specified in <i>artifact</i>.
+     *
+     * @param artifact The FLYSArtifact that stores the name of the river.
+     *
+     * @return the SRID as string (e.g. "31466").
+     */
+    public static String getRiverSrid(FLYSArtifact artifact) {
+        String river = artifact.getDataAsString("river");
+
+        if (river == null || river.length() == 0) {
+            return null;
+        }
+
+        return getRiverSrid(river);
+    }
+
+
+    public static String getRiverSrid(String rivername) {
+        Map<String, String> variables = new HashMap<String, String>(1);
+        variables.put("name", rivername);
+
+        Document cfg = Config.getConfig();
+
+        return (String) XMLUtils.xpath(
+            cfg,
+            XPATH_RIVER_PROJECTION,
+            XPathConstants.STRING,
+            null,
+            variables);
+    }
+
+
+    public static String createWspWTitle(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        name
+    ) {
+        String[] parts = name.split("=");
+
+        NumberFormat nf = Formatter.getWaterlevelW(cc);
+
+        String namedMainValue = null;
+
+        boolean isQ    = winfo.isQ();
+        boolean isFree = winfo.isFreeQ();
+
+        double v;
+
+        try {
+            v = Double.valueOf(parts[1]);
+
+            namedMainValue = getNamedMainValue(winfo.getGauge(), v);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Cannot parse Double of: '" + parts[1] + "'");
+            return name;
+        }
+
+        String prefix = null;
+
+        if (isQ && !isFree && namedMainValue != null) {
+            return "W (" + namedMainValue + ")";
+        }
+
+        if (isQ) {
+            prefix = "Q=";
+        }
+
+        return prefix == null
+            ? "W(" + nf.format(v) + ")"
+            : "W(" + prefix + nf.format(v) + ")";
+    }
+
+
+    public static String createWspQTitle(
+        WINFOArtifact winfo,
+        CallContext   cc,
+        String        name
+    ) {
+        String[] parts = name.split("=");
+
+        NumberFormat nf = Formatter.getWaterlevelQ(cc);
+
+        String namedMainValue = null;
+
+        boolean isQ    = winfo.isQ();
+        boolean isFree = winfo.isFreeQ();
+
+        double v;
+
+        try {
+            v = Double.valueOf(parts[1]);
+
+            namedMainValue = getNamedMainValue(winfo.getGauge(), v);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Cannot parse Double of: '" + parts[1] + "'");
+            return name;
+        }
+
+        String prefix = null;
+
+        if (isQ && !isFree && namedMainValue != null) {
+            return namedMainValue;
+        }
+
+        if (!isQ) {
+            prefix = "W=";
+        }
+
+        return prefix == null
+            ? "Q(" + nf.format(v) + ")"
+            : "Q(" + prefix + nf.format(v) + ")";
+    }
+
+
+    public static String getNamedMainValue(Gauge gauge, double value) {
+        List<MainValue> mainValues = gauge.getMainValues();
+        logger.debug("Search named main value for: " + value);
+
+        for (MainValue mv: mainValues) {
+            if (mv.getValue().doubleValue() == value) {
+                logger.debug("Found named main value: " + mv.getMainValue().getName());
+                return mv.getMainValue().getName();
+            }
+        }
+
+        logger.debug("Did not find a named main value for: " + value);
+        return null;
+    }
+
+
+    /**
+     * Returns the URL of user mapfile for the owner of Artifact
+     * <i>artifactId</i>.
+     *
+     * @param artifactId The UUID of an artifact.
+     *
+     * @return the URL of the user wms.
+     */
+    public static String getUserWMSUrl(String artifactId) {
+        String url = getXPathString(XPATH_MAPSERVER_URL);
+        url = url + "user-wms";
+
+        return 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-artifacts/src/main/java/de/intevation/flys/utils/Formatter.java	Fri Sep 28 12:14:51 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  = 2;
+
+
+    // 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/java/de/intevation/flys/utils/GeometryUtils.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,251 @@
+package de.intevation.flys.utils;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+
+import org.geotools.data.DataStoreFactorySpi;
+import org.geotools.data.FeatureStore;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.ShapefileDataStoreFactory;
+import org.geotools.feature.FeatureIterator;
+import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
+import org.geotools.geojson.feature.FeatureJSON;
+import org.geotools.referencing.CRS;
+
+import de.intevation.flys.model.RiverAxis;
+
+
+public class GeometryUtils {
+
+    private static final Logger logger = Logger.getLogger(GeometryUtils.class);
+
+
+    private GeometryUtils() {
+    }
+
+
+    public static Envelope getRiverBoundary(String rivername) {
+        RiverAxis axis = RiverAxis.getRiverAxis(rivername);
+        if (axis != null) {
+            // TODO Take the correct EPSG into account. Maybe, we need to
+            // reproject the geometry.
+            return axis.getGeom().getEnvelopeInternal();
+        }
+
+        return null;
+    }
+
+
+    public static String getRiverBounds(String rivername) {
+        Envelope env = getRiverBoundary(rivername);
+
+        if (env == null) {
+            return jtsBoundsToOLBounds(env);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the boundary of Envelope <i>env</i> in OpenLayers
+     * representation.
+     *
+     * @param env The envelope of a geometry.
+     *
+     * @return the OpenLayers boundary of <i>env</i>.
+     */
+    public static String jtsBoundsToOLBounds(Envelope env) {
+        return "" +
+            env.getMinX() + " " +
+            env.getMinY() + " " +
+            env.getMaxX() + " " +
+            env.getMaxY();
+    }
+
+
+    public static String createOLBounds(Geometry a, Geometry b) {
+        Coordinate[] ca = a.getCoordinates();
+        Coordinate[] cb = b.getCoordinates();
+
+        double lowerX = Double.MAX_VALUE;
+        double lowerY = Double.MAX_VALUE;
+        double upperX = -Double.MAX_VALUE;
+        double upperY = -Double.MAX_VALUE;
+
+        for (Coordinate c: ca) {
+            lowerX = lowerX < c.x ? lowerX : c.x;
+            lowerY = lowerY < c.y ? lowerY : c.y;
+
+            upperX = upperX > c.x ? upperX : c.x;
+            upperY = upperY > c.y ? upperY : c.y;
+        }
+
+        for (Coordinate c: cb) {
+            lowerX = lowerX < c.x ? lowerX : c.x;
+            lowerY = lowerY < c.y ? lowerY : c.y;
+
+            upperX = upperX > c.x ? upperX : c.x;
+            upperY = upperY > c.y ? upperY : c.y;
+        }
+
+        return "" + lowerX + " " + lowerY + " " + upperX + " " + upperY;
+    }
+
+
+    public static SimpleFeatureType buildFeatureType(
+        String name, String srs, Class geometryType)
+    {
+        return buildFeatureType(name, srs, geometryType, null);
+    }
+
+
+    /**
+     * Creates a new SimpleFeatureType using a SimpleFeatureTypeBuilder.
+     *
+     * @param name The name of the FeatureType.
+     * @param srs The SRS (e.g. "EPSG:31466").
+     * @param geometryType The geometry type's class (e.g. Polygon.class).
+     * @param attrs Optional. An object with attribute-name/attribute-class
+     * pairs where index 0 specifies the name as string and index 1 the type
+     * as class.
+     *
+     * @return a new SimpleFeatureType.
+     */
+    public static SimpleFeatureType buildFeatureType(
+        String name, String srs, Class geometryType, Object[][] attrs)
+    {
+        try {
+            SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
+            CoordinateReferenceSystem crs    = CRS.decode(srs);
+
+            builder.setName("flys");
+            builder.setNamespaceURI("http://www.intevation.de/");
+            builder.setCRS(crs);
+            builder.setSRS(srs);
+
+            builder.add("geometry", geometryType, crs);
+
+            if (attrs != null) {
+                for (Object[] attr: attrs) {
+                    builder.add((String) attr[0], (Class) attr[1]);
+                }
+            }
+
+            return builder.buildFeatureType();
+        }
+        catch (NoSuchAuthorityCodeException nsae) {
+        }
+        catch (FactoryException fe) {
+        }
+
+        return null;
+    }
+
+
+    public static List<SimpleFeature> parseGeoJSON(
+        String geojson, SimpleFeatureType ft
+    ) {
+        List<SimpleFeature> collection = new ArrayList<SimpleFeature>();
+
+        try {
+            FeatureJSON fjson = new FeatureJSON();
+            fjson.setFeatureType(ft);
+
+            FeatureIterator<SimpleFeature> iterator =
+                fjson.streamFeatureCollection(geojson);
+
+            while (iterator.hasNext()) {
+                collection.add(iterator.next());
+            }
+        }
+        catch (IOException ioe) {
+            // TODO handle exception
+        }
+
+        return collection;
+    }
+
+
+    public static boolean writeShapefile(
+        File              shape,
+        SimpleFeatureType featureType,
+        FeatureCollection collection
+    ) {
+        if (collection.isEmpty()) {
+            logger.warn("Shapefile is not written - no features given!");
+            return false;
+        }
+
+        Transaction transaction = null;
+
+        try {
+            Map<String, Serializable> params =
+                new HashMap<String, Serializable>();
+
+            params.put("url", shape.toURI().toURL());
+            params.put("create spatial index", Boolean.TRUE);
+
+            DataStoreFactorySpi dataStoreFactory =
+                new ShapefileDataStoreFactory();
+
+            ShapefileDataStore newDataStore =
+                (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
+            newDataStore.createSchema(featureType);
+
+            transaction = new DefaultTransaction("create");
+
+            String typeName = newDataStore.getTypeNames()[0];
+
+            FeatureStore<SimpleFeatureType, SimpleFeature> featureStore =
+                (FeatureStore<SimpleFeatureType, SimpleFeature>)
+                    newDataStore.getFeatureSource(typeName);
+
+            featureStore.setTransaction(transaction);
+
+            featureStore.addFeatures(collection);
+            transaction.commit();
+
+            return true;
+        }
+        catch (MalformedURLException mue) {
+            logger.error("Unable to prepare shapefile: " + mue.getMessage());
+        }
+        catch (IOException ioe) {
+            logger.error("Unable to write shapefile: " + ioe.getMessage());
+        }
+        finally {
+            if (transaction != null) {
+                try {
+                    logger.debug("XXX CLOSE TRANSACTION!");
+                    transaction.close();
+                }
+                catch (IOException ioe) { /* do nothing */ }
+            }
+        }
+
+        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/utils/MapfileGenerator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,670 @@
+package de.intevation.flys.utils;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+
+import de.intevation.artifacts.common.utils.Config;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.LayerInfo;
+import de.intevation.flys.artifacts.model.WMSLayerFacet;
+import de.intevation.flys.artifacts.model.WMSDBLayerFacet;
+
+/**
+ * This class iterates over a bunch of directories, searches for meta
+ * information coresponding to shapefiles and creates a mapfile which is used by
+ * a <i>MapServer</i>.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapfileGenerator
+extends Thread
+{
+    public static final String WSPLGEN_RESULT_SHAPE   = "wsplgen.shp";
+    public static final String WSPLGEN_LINES_SHAPE    = "barrier_lines.shp";
+    public static final String WSPLGEN_POLYGONS_SHAPE = "barrier_polygons.shp";
+
+    public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm";
+    public static final String DB_LAYER_TEMPLATE  = "db_layer.vm";
+
+    public static final String MS_WSPLGEN_POSTFIX  = "-wsplgen";
+    public static final String MS_BARRIERS_POSTFIX = "-barriers";
+    public static final String MS_LINE_POSTFIX     = "-lines";
+    public static final String MS_POLYGONS_POSTFIX = "-polygons";
+    public static final String MS_LAYER_PREFIX     = "ms_layer-";
+
+    protected static final long SLEEPTIME = 10 * 1000L; // 10 seconds
+
+    private static Logger logger = Logger.getLogger(MapfileGenerator.class);
+
+    private static MapfileGenerator instance;
+
+    private File mapfile;
+    private File shapefileDirectory;
+
+    private String mapServerUrl;
+    private String templatePath;
+    private String velocityLogfile;
+
+    private VelocityEngine velocityEngine;
+    private boolean lock[];
+
+
+
+    private MapfileGenerator() {
+        lock = new boolean[1];
+    }
+
+
+    /**
+     * A main method which can be used to create a mapfile without a running
+     * artifact server.<br>
+     * <b>NOTE:</b> This method is not implemented yet!
+     *
+     * @param args Arguments.
+     */
+    public static void main(String[] args) {
+        throw new Error("MapfileGenerator.main() is CURRENTLY NOT IMPLEMENTED!");
+    }
+
+
+    /**
+     * Returns the instance of this generator.
+     *
+     * @return the instance.
+     */
+    public static synchronized MapfileGenerator getInstance() {
+        if (instance == null) {
+            instance = new MapfileGenerator();
+            instance.setDaemon(true);
+            instance.start();
+        }
+
+        return instance;
+    }
+
+
+    /**
+     * Triggers the mapfile generation process.
+     */
+    public void update() {
+        synchronized (lock) {
+            logger.debug("update");
+            lock[0] = true;
+            lock.notify();
+        }
+    }
+
+
+    /**
+     * Thread to generate a mapfile.
+     */
+    @Override
+    public void run() {
+        logger.debug("Start MapfileGenerator thread...");
+        try {
+            for (;;) {
+                synchronized (lock) {
+                    while (!lock[0]) {
+                        lock.wait(SLEEPTIME);
+                    }
+                    lock[0] = false;
+                }
+
+                logger.debug("Start sync process now...");
+                generate();
+            }
+        }
+        catch (InterruptedException ie) {
+            logger.debug("MapfileGenerator thread got an interrupt.");
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.debug("Error while mapfile creation: " + fnfe.getMessage());
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        finally {
+            logger.debug("THREAD END");
+        }
+    }
+
+    /**
+     * Method to check the existance of a template file.
+     *
+     * @param templateID The name of a template.
+     * @return true, of the template exists - otherwise false.
+     */
+    public boolean templateExists(String templateID){
+        Template template = getTemplateByName(templateID);
+        return template != null;
+    }
+
+
+    /**
+     * Method which starts searching for meta information file and mapfile
+     * generation.
+     */
+    protected void generate()
+    throws    FileNotFoundException, IOException
+    {
+        File[]        userDirs = getUserDirs();
+
+        List<String> layers = parseLayers(userDirs);
+
+        logger.info("Found " + layers.size() + " layers for user mapfile.");
+
+        writeMapfile(layers);
+    }
+
+
+    /**
+     * Returns the VelocityEngine used for the template mechanism.
+     *
+     * @return the velocity engine.
+     */
+    protected VelocityEngine getVelocityEngine() {
+        if (velocityEngine == null) {
+            velocityEngine = new VelocityEngine();
+            try {
+                setupVelocity(velocityEngine);
+            }
+            catch (Exception e) {
+                logger.error(e, e);
+                return null;
+            }
+        }
+        return velocityEngine;
+    }
+
+
+    /**
+     * Initialize velocity.
+     *
+     * @param engine Velocity engine.
+     * @throws Exception if an error occured while initializing velocity.
+     */
+    protected void setupVelocity(VelocityEngine engine)
+    throws Exception
+    {
+        engine.setProperty(
+            "input.encoding",
+            "UTF-8");
+
+        engine.setProperty(
+            VelocityEngine.RUNTIME_LOG,
+            FLYSUtils.getXPathString(FLYSUtils.XPATH_VELOCITY_LOGFILE));
+
+        engine.setProperty(
+            "resource.loader",
+            "file");
+
+        engine.setProperty(
+            "file.resource.loader.path",
+            FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_TEMPLATE_PATH));
+
+        engine.init();
+    }
+
+
+    protected VelocityContext getVelocityContext() {
+        VelocityContext context = new VelocityContext();
+
+        try {
+            context.put("MAPSERVERURL",
+                FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_URL));
+            context.put("SHAPEFILEPATH",
+                getShapefileBaseDir().getCanonicalPath());
+            context.put("CONFIGDIR",
+                Config.getConfigDirectory().getCanonicalPath());
+        }
+        catch (FileNotFoundException fnfe) {
+            // this is bad
+        }
+        catch (IOException ioe) {
+            // this is also bad
+        }
+
+        return context;
+    }
+
+
+    /**
+     * Returns a template specified by <i>model</i>.
+     *
+     * @param model The name of the template.
+     * @return a template.
+     */
+    protected Template getTemplateByName(String model) {
+        if (model.indexOf(".vm") < 0) {
+            model = model.concat(".vm");
+        }
+
+        try {
+            VelocityEngine engine = getVelocityEngine();
+            if (engine == null) {
+                logger.error("Error while fetching VelocityEngine.");
+                return null;
+            }
+
+            return engine.getTemplate(model);
+        }
+        catch (Exception e) {
+            logger.warn(e, e);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the mapfile  template.
+     *
+     * @return the mapfile template.
+     * @throws Exception if an error occured while reading the configuration.
+     */
+    protected Template getMapfileTemplate()
+    throws Exception
+    {
+        String mapfileName = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_MAPFILE_TEMPLATE);
+
+        return getTemplateByName(mapfileName);
+    }
+
+
+    /**
+     * Returns the base directory storing the shapefiles.
+     *
+     * @return the shapefile base directory.
+     *
+     * @throws FileNotFoundException if no shapefile path is found or
+     * configured.
+     */
+    public File getShapefileBaseDir()
+    throws    FileNotFoundException, IOException
+    {
+        if (shapefileDirectory == null) {
+            String path = FLYSUtils.getXPathString(
+                FLYSUtils.XPATH_SHAPEFILE_DIR);
+
+            if (path != null) {
+                shapefileDirectory = new File(path);
+            }
+
+            if (shapefileDirectory == null) {
+                throw new FileNotFoundException("No shapefile directory given");
+            }
+
+            if (!shapefileDirectory.exists()) {
+                shapefileDirectory.createNewFile();
+            }
+        }
+
+        return shapefileDirectory;
+    }
+
+
+    protected File[] getUserDirs()
+    throws    FileNotFoundException, IOException
+    {
+        File   baseDir      = getShapefileBaseDir();
+        File[] artifactDirs = baseDir.listFiles();
+
+        // TODO ONLY RETURN DIRECTORIES OF THE SPECIFIED USER
+
+        return artifactDirs;
+    }
+
+
+
+    protected List<String> parseLayers(File[] dirs) {
+        List<String> layers = new ArrayList<String>();
+
+        for (File dir: dirs) {
+            File[] layerFiles = dir.listFiles(new FilenameFilter() {
+                @Override
+                public boolean accept(File directory, String name) {
+                    return name.startsWith(MS_LAYER_PREFIX);
+                }
+            });
+
+            for (File layer: layerFiles) {
+                try {
+                    layers.add(layer.getCanonicalPath());
+                }
+                catch (IOException ioe) {
+                    logger.warn(ioe, ioe);
+                }
+            }
+        }
+
+        return layers;
+    }
+
+
+    /**
+     * Creates a layer file used for Mapserver's mapfile which represents the
+     * floodmap.
+     *
+     * @param flys The FLYSArtifact that owns <i>wms</i>.
+     * @param wms The WMSLayerFacet that contains information for the layer.
+     */
+    public void createUeskLayer(FLYSArtifact flys, WMSLayerFacet wms)
+    throws FileNotFoundException, IOException
+    {
+        logger.debug("createUeskLayer");
+
+        LayerInfo layerinfo = new LayerInfo();
+        layerinfo.setName(flys.identifier() + MS_WSPLGEN_POSTFIX);
+        layerinfo.setType("POLYGON");
+        layerinfo.setDirectory(flys.identifier());
+        layerinfo.setData(WSPLGEN_RESULT_SHAPE);
+        layerinfo.setTitle("I18N_WSPLGEN_RESULT");
+
+        String name = MS_LAYER_PREFIX + wms.getName();
+
+        Template template = getTemplateByName(SHP_LAYER_TEMPLATE);
+        if (template == null) {
+            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
+            return;
+        }
+
+        try {
+            File dir = new File(getShapefileBaseDir(), flys.identifier());
+            writeLayer(layerinfo, dir, name, template);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+            logger.warn("Unable to write layer: " + name);
+        }
+    }
+
+
+    /**
+     * Creates a layer file used for Mapserver's mapfile which represents the
+     * user defined barriers.
+     *
+     * @param flys The FLYSArtifact that owns <i>wms</i>.
+     * @param wms The WMSLayerFacet that contains information for the layer.
+     */
+    public void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms)
+    throws FileNotFoundException, IOException
+    {
+        logger.debug("createBarriersLayer");
+
+        String uuid = flys.identifier();
+        File   dir  = new File(getShapefileBaseDir(), uuid);
+
+        createBarriersLineLayer(flys, wms);
+        createBarriersPolygonLayer(flys, wms);
+    }
+
+
+    protected void createBarriersLineLayer(
+        FLYSArtifact  flys,
+        WMSLayerFacet wms
+    )
+    throws FileNotFoundException, IOException
+    {
+        String uuid       = flys.identifier();
+        String group      = uuid + MS_BARRIERS_POSTFIX;
+        String groupTitle = "I18N_BARRIERS_TITLE";
+
+        File dir  = new File(getShapefileBaseDir(), uuid);
+        File test = new File(dir, WSPLGEN_LINES_SHAPE);
+
+        if (!test.exists() || !test.canRead()) {
+            logger.debug("No barrier line layer existing.");
+            return;
+        }
+
+        LayerInfo lineInfo = new LayerInfo();
+        lineInfo.setName(uuid + MS_LINE_POSTFIX);
+        lineInfo.setType("LINE");
+        lineInfo.setDirectory(uuid);
+        lineInfo.setData(WSPLGEN_LINES_SHAPE);
+        lineInfo.setTitle("I18N_LINE_SHAPE");
+        lineInfo.setGroup(group);
+        lineInfo.setGroupTitle(groupTitle);
+
+        String nameLines = MS_LAYER_PREFIX + wms.getName() + "-lines";
+
+        Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
+        if (tpl == null) {
+            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
+            return;
+        }
+
+        try {
+            writeLayer(lineInfo, dir, nameLines, tpl);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+            logger.warn("Unable to write layer: " + nameLines);
+        }
+    }
+
+
+    protected void createBarriersPolygonLayer(
+        FLYSArtifact  flys,
+        WMSLayerFacet wms
+    )
+    throws FileNotFoundException, IOException
+    {
+        String uuid       = flys.identifier();
+        String group      = uuid + MS_BARRIERS_POSTFIX;
+        String groupTitle = "I18N_BARRIERS_TITLE";
+
+        File dir  = new File(getShapefileBaseDir(), uuid);
+        File test = new File(dir, WSPLGEN_POLYGONS_SHAPE);
+
+        if (!test.exists() || !test.canRead()) {
+            logger.debug("No barrier line layer existing.");
+            return;
+        }
+
+        LayerInfo polygonInfo = new LayerInfo();
+        polygonInfo.setName(uuid + MS_POLYGONS_POSTFIX);
+        polygonInfo.setType("POLYGON");
+        polygonInfo.setDirectory(uuid);
+        polygonInfo.setData(WSPLGEN_POLYGONS_SHAPE);
+        polygonInfo.setTitle("I18N_POLYGON_SHAPE");
+        polygonInfo.setGroup(group);
+        polygonInfo.setGroupTitle(groupTitle);
+
+        String namePolygons = MS_LAYER_PREFIX + wms.getName() + "-polygons";
+
+        Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
+        if (tpl == null) {
+            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
+            return;
+        }
+
+        try {
+            writeLayer(polygonInfo, dir, namePolygons, tpl);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+            logger.warn("Unable to write layer: " + namePolygons);
+        }
+    }
+
+
+    /**
+     * Creates a layer file used for Mapserver's mapfile which represents
+     * geometries from database.
+     *
+     * @param flys The FLYSArtifact that owns <i>wms</i>.
+     * @param wms The WMSLayerFacet that contains information for the layer.
+     */
+    public void createDatabaseLayer(
+        FLYSArtifact    flys,
+        WMSDBLayerFacet wms,
+        String          style
+    )
+    throws FileNotFoundException, IOException
+    {
+        logger.debug("createDatabaseLayer");
+
+        LayerInfo layerinfo = new LayerInfo();
+        layerinfo.setName(flys.identifier() + "-" + wms.getName());
+        layerinfo.setType(wms.getGeometryType());
+        layerinfo.setFilter(wms.getFilter());
+        layerinfo.setData(wms.getData());
+        layerinfo.setTitle(wms.getDescription());
+        layerinfo.setStyle(style);
+        layerinfo.setExtent(GeometryUtils.jtsBoundsToOLBounds(wms.getExtent()));
+
+        String name = MS_LAYER_PREFIX + wms.getName();
+
+        Template template = getTemplateByName(DB_LAYER_TEMPLATE);
+        if (template == null) {
+            logger.warn("Template '" + DB_LAYER_TEMPLATE + "' found.");
+            return;
+        }
+
+        try {
+            File dir = new File(getShapefileBaseDir(), flys.identifier());
+            writeLayer(layerinfo, dir, name, template);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+            logger.warn("Unable to write layer: " + name);
+        }
+    }
+
+
+    /**
+     * Creates a layer snippet which might be included in the mapfile.
+     *
+     * @param layerinfo A LayerInfo object that contains all necessary
+     * information to build a Mapserver LAYER section.
+     * @param dir The base dir for the LAYER snippet.
+     * @param filename The name of the file that is written.
+     * @param tpl The Velocity template which is used to create the LAYER
+     * section.
+     */
+    protected void writeLayer(
+        LayerInfo layerinfo,
+        File      dir,
+        String    filename,
+        Template  tpl
+    )
+    throws    FileNotFoundException
+    {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Write layer for:");
+            logger.debug("   directory: " + dir.getName());
+            logger.debug("   name:      " + filename);
+        }
+
+        File   layer  = new File(dir, filename);
+        Writer writer = null;
+
+        try {
+            writer = new FileWriter(layer);
+
+            VelocityContext context = getVelocityContext();
+            context.put("LAYER", layerinfo);
+
+            tpl.merge(context, writer);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+            }
+            catch (IOException ioe) {
+                logger.debug(ioe, ioe);
+            }
+        }
+    }
+
+
+    /**
+     * Creates a mapfile with the layer information stored in <i>layers</i>.
+     *
+     * @param layers Layer information.
+     */
+    protected void writeMapfile(List<String> layers) {
+        String tmpMapName = "mapfile" + new Date().getTime();
+
+        File mapfile = new File(
+            FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPFILE_PATH));
+
+        File   tmp     = null;
+        Writer writer  = null;
+
+        try {
+            tmp = new File(mapfile.getParent(), tmpMapName);
+            tmp.createNewFile();
+
+            writer = new FileWriter(tmp);
+
+            VelocityContext context = getVelocityContext();
+            context.put("LAYERS", layers);
+
+            Template mapTemplate = getMapfileTemplate();
+            if (mapTemplate == null) {
+                logger.warn("No mapfile template found.");
+                return;
+            }
+
+            mapTemplate.merge(context, writer);
+
+            // we need to create a temporary mapfile first und rename it into
+            // real mapfile because we don't run into race conditions on this
+            // way. (iw)
+            tmp.renameTo(mapfile);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+
+                if (tmp.exists()) {
+                    tmp.delete();
+                }
+            }
+            catch (IOException ioe) {
+                logger.debug(ioe, 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/utils/Pair.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package de.intevation.flys.utils;
+
+import java.io.Serializable;
+
+/**
+ * @param <A>
+ * @param <B>
+ * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
+ */
+public final class Pair<A, B>
+implements         Serializable
+{
+    private A a;
+    private B b;
+
+    private Pair() {
+    }
+
+    public Pair(A a, B b) {
+        this.a = a;
+        this.b = b;
+    }
+
+    public A getA() {
+        return a;
+    }
+
+    public B getB() {
+        return b;
+    }
+}
+// vim: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/ThemeUtil.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,331 @@
+package de.intevation.flys.utils;
+
+import org.apache.log4j.Logger;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.model.MapserverStyle;
+import de.intevation.flys.artifacts.model.MapserverStyle.Clazz;
+
+
+/**
+ * Utility to deal with themes and their representations.
+ */
+public class ThemeUtil {
+
+    private static Logger logger =
+        Logger.getLogger(ThemeUtil.class);
+
+    public final static String XPATH_LINE_COLOR =
+        "/theme/field[@name='linecolor']/@default";
+
+    public static final String XPATH_LINE_SIZE =
+        "/theme/field[@name='linesize']/@default";
+
+    public static final String XPATH_LINE_STYLE =
+        "/theme/field[@name='linetype']/@default";
+
+    public final static String XPATH_SHOW_POINTS =
+        "/theme/field[@name='showpoints']/@default";
+
+    public final static String XPATH_SHOW_LINE =
+        "/theme/field[@name='showlines']/@default";
+
+    public final static String XPATH_TEXT_COLOR =
+        "/theme/field[@name='textcolor']/@default";
+
+    public final static String XPATH_TEXT_SIZE =
+        "/theme/field[@name='textsize']/@default";
+
+    public final static String XPATH_TEXT_FONT =
+        "/theme/field[@name='font']/@default";
+
+    public final static String XPATH_TEXT_STYLE =
+        "/theme/field[@name='textstyle']/@default";
+
+    public final static String XPATH_TEXT_ORIENTATION =
+        "/theme/field[@name='textorientation']/@default";
+
+    public final static String XPATH_TEXT_BACKGROUND =
+        "/theme/field[@name='backgroundcolor']/@default";
+
+    public final static String XPATH_SHOW_BACKGROUND =
+        "/theme/field[@name='showbackground']/@default";
+
+    public final static String XPATH_SYMBOL =
+        "/theme/field[@name='symbol']/@default";
+
+    /**
+     * Parses line width, defaulting to 0.
+     * @param theme the theme
+     */
+    public static int parseLineWidth(Document theme) {
+        String size = XMLUtils.xpathString(theme, XPATH_LINE_SIZE, null);
+        if (size == null || size.length() == 0) {
+            return 0;
+        }
+
+        try {
+            return Integer.valueOf(size);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Unable to set line size from string: '" + size + "'");
+        }
+        return 0;
+    }
+
+
+    /**
+     * Parses the line style, defaulting to '10'.
+     * @param theme The theme.
+     */
+    public static float[] parseLineStyle(Document theme) {
+        String dash = XMLUtils.xpathString(theme, XPATH_LINE_STYLE, null);
+
+        float[] def = {10};
+        if (dash == null || dash.length() == 0) {
+            return def;
+        }
+
+        String[] pattern = dash.split(",");
+        if(pattern.length == 1) {
+            return def;
+        }
+
+        try {
+            float[] dashes = new float[pattern.length];
+            for (int i = 0; i < pattern.length; i++) {
+                dashes[i] = Float.parseFloat(pattern[i]);
+            }
+            return dashes;
+        }
+        catch(NumberFormatException nfe) {
+            logger.warn("Unable to set dash from string: '" + dash + "'");
+            return def;
+        }
+    }
+
+
+    /**
+     * Parses text size, defaulting to 10.
+     * @param theme The theme.
+     */
+    public static int parseTextSize(Document theme) {
+        String size = XMLUtils.xpathString(theme, XPATH_TEXT_SIZE, null);
+        if (size == null || size.length() == 0) {
+            return 10;
+        }
+
+        try {
+            return Integer.valueOf(size);
+        }
+        catch (NumberFormatException nfe) {
+        }
+        return 10;
+    }
+
+
+    /**
+     * Parses the attribute 'showpoints', defaults to false.
+     * @param theme The theme.
+     */
+    public static boolean parseShowPoints(Document theme) {
+        String show = XMLUtils.xpathString(theme, XPATH_SHOW_POINTS, null);
+        if (show == null || show.length() == 0) {
+            return false;
+        }
+        if (show.equals("true")) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Parses the attribute 'showlines', defaults to true.
+     * @param theme The theme.
+     */
+    public static boolean parseShowLine(Document theme) {
+        String show = XMLUtils.xpathString(theme, XPATH_SHOW_LINE, null);
+        if (show == null || show.length() == 0) {
+            return true;
+        }
+        if (show.equals("false")) {
+            return false;
+        }
+        else {
+            return true;
+        }
+    }
+
+
+    /**
+     * Parses text color.
+     * @param theme The theme.
+     */
+    public static Color parseTextColor(Document theme) {
+        String color = XMLUtils.xpathString(theme, XPATH_TEXT_COLOR, null);
+        return parseRGB(color);
+    }
+
+
+    /**
+     * Parses the font.
+     * @param theme The theme.
+     */
+    public static Font parseTextFont(Document theme) {
+        String font = XMLUtils.xpathString(theme, XPATH_TEXT_FONT, null);
+        if (font == null || font.length() == 0) {
+            return null;
+        }
+
+        int size = parseTextSize(theme);
+        int style = parseTextStyle(theme);
+        Font f = new Font (font, style, size);
+        return f;
+    }
+
+
+    /**
+     * Parses the text style, defaults to 'Font.PLAIN'.
+     * @param theme The theme.
+     */
+    public static int parseTextStyle(Document theme) {
+        String style = XMLUtils.xpathString(theme, XPATH_TEXT_STYLE, null);
+        if (style == null || style.length() == 0) {
+            return Font.PLAIN;
+        }
+
+        if (style.equals("italic")) {
+            return Font.ITALIC;
+        }
+        else if (style.equals("bold")) {
+            return Font.BOLD;
+        }
+        else {
+            return Font.PLAIN;
+        }
+    }
+
+
+    /**
+     * Parses the textorientation, defaults to 'vertical'.
+     * @param theme The theme.
+     */
+    public static String parseTextOrientation(Document theme) {
+        String o = XMLUtils.xpathString(theme, XPATH_TEXT_ORIENTATION, null);
+        if (o == null || o.length() == 0) {
+            return "vertical";
+        }
+        if(o.equals("true")) {
+            return "horizontal";
+        }
+        else {
+            return "vertical";
+        }
+    }
+
+
+    /**
+     * Parses the text background color, defaults to white.
+     * @param theme The theme.
+     */
+    public static Color parseTextBackground(Document theme) {
+        String color = XMLUtils.xpathString(theme, XPATH_TEXT_BACKGROUND, null);
+        if (color == null || color.length() == 0) {
+            return Color.WHITE;
+        }
+        return parseRGB(color);
+    }
+
+
+    /**
+     * Parses the attribute whether to show background or not, defaults to
+     * false.
+     * @param theme The theme.
+     */
+    public static boolean parseShowTextBackground(Document theme) {
+        String show = XMLUtils.xpathString(theme, XPATH_SHOW_BACKGROUND, null);
+        if(show == null || show.length() == 0) {
+            return false;
+        }
+
+        if(show.equals("true")) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Parse a string like "103, 100, 0" and return a corresping color.
+     * @param rgbtext Color as string representation, e.g. "255,0,20".
+     * @return Color, null in case of issues.
+     */
+    public static Color parseRGB(String rgbtext) {
+        if (rgbtext == null) {
+            return null;
+        }
+        String rgb[] = rgbtext.split(",");
+        Color c = null;
+        try {
+            c = new Color(
+                    Integer.valueOf(rgb[0].trim()),
+                    Integer.valueOf(rgb[1].trim()),
+                    Integer.valueOf(rgb[2].trim()));
+        }
+        catch (NumberFormatException nfe) {
+            c = null;
+        }
+        return c;
+    }
+
+
+    public static String getLineColorString(Document theme) {
+        return XMLUtils.xpathString(theme, XPATH_LINE_COLOR, null);
+    }
+
+
+    public static String getSymbol(Document theme) {
+        return XMLUtils.xpathString(theme, XPATH_SYMBOL, null);
+    }
+
+
+    /**
+     * Gets color from color field.
+     * @param theme    the theme document.
+     * @return color.
+     */
+    public static Color parseLineColorField(Document theme) {
+        return parseRGB(getLineColorString(theme));
+    }
+
+
+    public static String createMapserverStyle(Document theme) {
+        String symbol    = getSymbol(theme);
+        String linecolor = getLineColorString(theme);
+        linecolor        = linecolor.replace(",", "");
+
+        int linewidth = parseLineWidth(theme);
+
+        MapserverStyle ms = new MapserverStyle();
+
+        Clazz c = new Clazz(" ");
+        c.setOutlineColor(linecolor);
+        c.setSize(linewidth);
+        c.setSymbol(symbol);
+
+        ms.addClazz(c);
+
+        return ms.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/FacetCreator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,151 @@
+package de.intevation.flys.wsplgen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.model.CrossSectionTrack;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WMSLayerFacet;
+import de.intevation.flys.artifacts.resources.Resources;
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+import de.intevation.flys.utils.FLYSUtils;
+import de.intevation.flys.utils.GeometryUtils;
+import de.intevation.flys.utils.MapfileGenerator;
+
+
+public class FacetCreator implements FacetTypes {
+
+    public static final String I18N_WSPLGEN_RESULT   = "floodmap.uesk";
+    public static final String I18N_WSPLGEN_DEFAULT  = "floodmap.uesk";
+    public static final String I18N_BARRIERS         = "floodmap.barriers";
+    public static final String I18N_BARRIERS_DEFAULT = "floodmap.barriers";
+
+    protected FLYSArtifact artifact;
+
+    protected CallContext  cc;
+
+    protected List<Facet> facets;
+    protected List<Facet> tmpFacets;
+
+    protected String url;
+    protected String hash;
+    protected String stateId;
+
+    public FacetCreator(
+        FLYSArtifact artifact,
+        CallContext  cc,
+        String       hash,
+        String       sId,
+        List<Facet>  facets
+    ) {
+        this.tmpFacets  = new ArrayList<Facet>(2);
+        this.facets     = facets;
+        this.artifact   = artifact;
+        this.cc         = cc;
+        this.hash       = hash;
+        this.stateId    = sId;
+    }
+
+    protected String getRiver() {
+        return artifact.getDataAsString("river");
+    }
+
+    protected String getUrl() {
+        return FLYSUtils.getUserWMSUrl(artifact.identifier());
+    }
+
+    protected String getSrid() {
+        return FLYSUtils.getRiverSrid(artifact);
+    }
+
+    protected Envelope getWSPLGENBounds() {
+        String river = getRiver();
+        double kms[] = FLYSUtils.getKmRange(artifact);
+
+        CrossSectionTrack a =
+            CrossSectionTrack.getCrossSectionTrack(river, kms[0]);
+
+        CrossSectionTrack b =
+            CrossSectionTrack.getCrossSectionTrack(river, kms[1]);
+
+        if (a == null || b == null) {
+            return null;
+        }
+
+        Envelope envA = a.getGeom().getEnvelopeInternal();
+        Envelope envB = b.getGeom().getEnvelopeInternal();
+
+        envA.expandToInclude(envB);
+
+        return envA;
+    }
+
+    protected Envelope getBounds() {
+        return GeometryUtils.getRiverBoundary(getRiver());
+    }
+
+    public List<Facet> getFacets() {
+        return tmpFacets;
+    }
+
+    public void createWSPLGENFacet() {
+        WMSLayerFacet wsplgen = new WMSLayerFacet(
+            0,
+            FLOODMAP_WSPLGEN,
+            Resources.getMsg(
+                cc.getMeta(),
+                I18N_WSPLGEN_RESULT,
+                I18N_WSPLGEN_DEFAULT),
+            ComputeType.ADVANCE,
+            stateId,
+            hash,
+            getUrl());
+
+        Envelope bounds = getWSPLGENBounds();
+
+        if (bounds == null) {
+            bounds = getBounds();
+        }
+
+        wsplgen.addLayer(
+            artifact.identifier() + MapfileGenerator.MS_WSPLGEN_POSTFIX);
+        wsplgen.setSrid(getSrid());
+        wsplgen.setExtent(bounds);
+
+        tmpFacets.add(wsplgen);
+    }
+
+    public void createBarrierFacet() {
+        WMSLayerFacet barriers = new WMSLayerFacet(
+            1,
+            FLOODMAP_BARRIERS,
+            Resources.getMsg(
+                cc.getMeta(),
+                I18N_BARRIERS,
+                I18N_BARRIERS_DEFAULT),
+            ComputeType.ADVANCE,
+            stateId,
+            hash,
+            getUrl());
+
+        barriers.addLayer(
+            artifact.identifier() + MapfileGenerator.MS_BARRIERS_POSTFIX);
+        barriers.setSrid(getSrid());
+        barriers.setExtent(getBounds());
+
+        tmpFacets.add(barriers);
+    }
+
+
+    public void finish() {
+        facets.addAll(getFacets());
+    }
+} // end of FacetCreator
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/JobExecutor.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,113 @@
+package de.intevation.flys.wsplgen;
+
+import java.io.IOException;
+import java.io.File;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.model.WSPLGENJob;
+
+
+public class JobExecutor {
+
+    public static final String WSPLGEN_PARAMETER_FILE =
+        "wsplgen.par";
+
+    public static final String WSPLGEN_BIN_PATH =
+        System.getProperty("wsplgen.bin.path");
+
+
+    private Logger logger = Logger.getLogger(JobExecutor.class);
+
+    private Process process;
+
+    protected WSPLGENJob job;
+
+    protected JobObserver     logObserver;
+    protected ProblemObserver errorObserver;
+
+
+    public JobExecutor(WSPLGENJob job) {
+        this.job           = job;
+        this.logObserver   = new JobObserver(job);
+        this.errorObserver = new ProblemObserver(job);
+    }
+
+
+    public void execute() {
+        File dir       = job.getWorkingDir();
+        File parameter = new File(dir, WSPLGEN_PARAMETER_FILE);
+
+        String[] args = new String[] {
+            WSPLGEN_BIN_PATH,
+            "-PAR=\"" + parameter.getAbsolutePath() + "\""
+        };
+
+        execute(args, dir);
+    }
+
+
+    protected void execute(String[] args, File dir) {
+        logger.info("Start JobExecutor for artifact: " + dir.getName());
+
+        String errorMsg = null;
+
+        try {
+            synchronized (this) {
+                process = Runtime.getRuntime().exec(args, null, dir);
+
+                logObserver.setInputStream(process.getInputStream());
+                errorObserver.setInputStream(process.getErrorStream());
+
+                logObserver.start();
+                errorObserver.start();
+
+                try {
+                    process.waitFor();
+                }
+                catch (InterruptedException ie) {
+                    logger.error("WSPLGEN job interrupted: " + ie.getMessage());
+                }
+
+                try {
+                    logObserver.join();
+                    errorObserver.join();
+                }
+                catch (InterruptedException iee) { /* do nothing */ }
+
+                logger.info("WSPLGEN exit value: " + process.exitValue());
+                logger.info(
+                    "WSPLGEN throw " +
+                    errorObserver.numErrors() + " errors.");
+                logger.info(
+                    "WSPLGEN throw " +
+                    errorObserver.numWarnings() + " warnings.");
+
+                if (process.exitValue() < 2 && errorObserver.numErrors() == 0) {
+                    FacetCreator fc = job.getFacetCreator();
+                    fc.createWSPLGENFacet();
+                    fc.finish();
+                }
+
+                job.getCallContext().afterBackground(CallContext.STORE);
+
+                return;
+            }
+        }
+        catch (SecurityException se) {
+            logger.error(se);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe);
+        }
+        catch (NullPointerException npe) {
+            logger.error(npe, npe);
+        }
+        catch (IndexOutOfBoundsException ioobe) {
+            logger.error(ioobe, ioobe);
+        }
+    }
+}
+// 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/wsplgen/JobObserver.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,106 @@
+package de.intevation.flys.wsplgen;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.artifacts.model.CalculationMessage;
+import de.intevation.flys.artifacts.model.WSPLGENJob;
+
+
+public class JobObserver extends Thread {
+
+    private static Logger logger = Logger.getLogger(JobObserver.class);
+
+
+    public static final String WSPLGEN_ENCODING =
+        "ISO-8859-1";
+
+    public static final String WSPLGEN_LOG_OUTPUT =
+        System.getProperty("wsplgen.log.output", "false");
+
+    public static final String[] STEPS = {
+        ".*<-Auswertung der Kommandozeilen-Parameter beendet.*",
+        ".*->Laden des DGM in Datei '.*' gestartet.*",
+        ".*->Triangulierung der Knoten gestartet.*",
+        ".*->Anpassung der Elemente an Dämme und Gräben gestartet.*",
+        ".*<-WSPLGEN Version .* beendet.*"
+    };
+
+
+    protected WSPLGENJob job;
+
+    protected InputStream in;
+
+    protected Pattern[] patterns;
+
+    protected int len;
+
+    protected boolean copy;
+
+
+    public JobObserver(WSPLGENJob job) {
+        this.job  = job;
+        this.len  = 0;
+        this.copy = Boolean.parseBoolean(WSPLGEN_LOG_OUTPUT);
+
+        patterns = new Pattern[STEPS.length];
+    }
+
+
+    protected void prepareRegexes() {
+        for (int num = STEPS.length, i = 0; i < num; i++) {
+            patterns[i] = Pattern.compile(STEPS[i], Pattern.DOTALL);
+        }
+    }
+
+
+    public void setInputStream(InputStream in) {
+        this.in = in;
+    }
+
+
+    public void run() {
+        logger.debug("Start observation...");
+        prepareRegexes();
+
+        try {
+            BufferedReader reader =
+                new BufferedReader(
+                    new InputStreamReader(in, WSPLGEN_ENCODING));
+
+            String line = null;
+
+            while ((line = reader.readLine()) != null) {
+                if (copy) {
+                    logger.debug(line);
+                }
+
+                update(line);
+            }
+        }
+        catch (IOException ioe) {
+            logger.warn("Observation canceled: " + ioe.getMessage());
+        }
+    }
+
+
+    protected void update(String log) {
+        for (int num = patterns.length, i = 0; i < num; i++) {
+            Matcher m = patterns[i].matcher(log);
+
+            if (m.matches()) {
+                job.getCallContext().addBackgroundMessage(
+                    new CalculationMessage(num, i+1, log));
+
+                logger.info("Finished step " + (i+1) + " / " + num);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,115 @@
+package de.intevation.flys.wsplgen;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.artifacts.model.WSPLGENCalculation;
+import de.intevation.flys.artifacts.model.WSPLGENJob;
+
+
+public class ProblemObserver extends JobObserver {
+
+    private static Logger logger = Logger.getLogger(ProblemObserver.class);
+
+
+    public static final Pattern WSPLGEN_ERROR_START = Pattern.compile(
+        ".*->(.*Fehler)(\\s*\\((\\d+)\\).*)*",
+        Pattern.DOTALL);
+
+    public static final Pattern WSPLGEN_ERROR_END = Pattern.compile(
+        ".*<-(.*Fehler).*",
+        Pattern.DOTALL);
+
+    public static final Pattern WSPLGEN_WARNING_START = Pattern.compile(
+        ".*->Warnung\\s*\\((\\d+)\\).*",
+        Pattern.DOTALL);
+
+    public static final Pattern WSPLGEN_WARNING_END = Pattern.compile(
+        ".*<-Warnung .*",
+        Pattern.DOTALL);
+
+
+    protected int error;
+    protected int warning;
+
+    protected WSPLGENCalculation calculation;
+
+
+    public ProblemObserver(WSPLGENJob job) {
+        super(job);
+        error       = -1;
+        warning     = -1;
+        calculation = job.getCalculation();
+    }
+
+
+    public void run() {
+        logger.debug("Start observation...");
+
+        super.run();
+    }
+
+
+    @Override
+    protected void prepareRegexes() {
+        // do nothing
+    }
+
+
+    @Override
+    protected void update(String log) {
+        Matcher startError = WSPLGEN_ERROR_START.matcher(log);
+        if (startError.matches()) {
+            if (startError.groupCount() >= 2) {
+                String tmp = startError.group(3);
+
+                if (tmp != null && tmp.length() > 0) {
+                    error = Integer.parseInt(tmp);
+                }
+                else error = 1;
+            }
+            else {
+                error = 1;
+            }
+
+            return;
+        }
+
+        Matcher endError = WSPLGEN_ERROR_END.matcher(log);
+        if (endError.matches()) {
+            error = -1;
+        }
+
+        if (error > 0) {
+            calculation.addError(new Integer(error), log);
+        }
+
+        Matcher startWarning = WSPLGEN_WARNING_START.matcher(log);
+        if (startWarning.matches()) {
+            warning = Integer.parseInt(startWarning.group(1));
+            return;
+        }
+
+        Matcher endWarning = WSPLGEN_WARNING_END.matcher(log);
+        if (endWarning.matches()) {
+            warning = -1;
+        }
+
+        if (warning > 0) {
+            calculation.addWarning(new Integer(warning), log);
+        }
+    }
+
+
+    public int numErrors() {
+        return calculation.numErrors();
+    }
+
+
+    public int numWarnings() {
+        return calculation.numWarnings();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/Scheduler.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,109 @@
+package de.intevation.flys.wsplgen;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.artifacts.model.WSPLGENJob;
+
+
+public class Scheduler implements Runnable {
+
+    public static final int MAX_WSPLGEN_PROCESSES = 1;
+
+
+    protected List<WSPLGENJob> jobs;
+
+
+    private static Scheduler INSTANCE;
+
+    private static final Logger logger = Logger.getLogger(Scheduler.class);
+
+
+
+    private Scheduler() {
+        jobs = Collections.synchronizedList(new LinkedList<WSPLGENJob>());
+    }
+
+
+    public static Scheduler getInstance() {
+        if (INSTANCE == null) {
+            logger.info("Create new WSPLGEN Scheduler...");
+
+            INSTANCE = new Scheduler();
+            new Thread(INSTANCE).start();
+        }
+
+        return INSTANCE;
+    }
+
+
+    public void addJob(WSPLGENJob job) {
+        synchronized(jobs) {
+            jobs.add(job);
+
+            logger.info("New WSPLGEN job added.");
+
+            jobs.notifyAll();
+        }
+    }
+
+
+    public WSPLGENJob getJob() {
+        synchronized(jobs) {
+            if (!jobs.isEmpty()) {
+                return jobs.remove(0);
+            }
+
+            return null;
+        }
+    }
+
+
+    public void run() {
+        logger.info("WSPLGEN Scheduler started.");
+
+        for (;;) {
+            try {
+                doRun();
+            }
+            catch (InterruptedException ie) {
+                logger.warn("Interrupt in WSPLGEN Scheduler -> restart it!");
+            }
+        }
+    }
+
+
+    public void doRun()
+    throws InterruptedException
+    {
+        for (;;) {
+            final WSPLGENJob job = getJob();
+
+            if (job != null) {
+                logger.debug("Got new job to execute...");
+
+                Thread t = new Thread() {
+                    public void run() {
+                        JobExecutor executor = new JobExecutor(job);
+                        executor.execute();
+                    }
+                };
+
+                t.start();
+                t.join();
+            }
+            else {
+                logger.info("No more jobs in Scheduler -> go sleep!");
+                synchronized (jobs) {
+                    jobs.wait();
+                }
+
+                logger.info("New jobs in Scheduler -> wake up!");
+            }
+        }
+    }
+}
+// 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/resources/datacage-sql/org-h2-driver.properties	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,29 @@
+delete.all.users = DELETE FROM users
+delete.all.artifacts = DELETE FROM artifacts
+user.id.nextval = SELECT NEXTVAL('USERS_ID_SEQ')
+user.by.gid = SELECT id FROM users WHERE gid = ?
+insert.user = INSERT INTO users (id, gid) VALUES (?, ?)
+collection.by.gid = SELECT id FROM collections WHERE gid = ?
+collection.id.nextval = SELECT NEXTVAL('COLLECTIONS_ID_SEQ')
+insert.collection = INSERT INTO collections (id, gid, user_id, name, creation) VALUES (?, ?, ?, ?, ?)
+artifact.by.gid = SELECT id FROM artifacts WHERE gid = ?
+collection.item.id.nextval = SELECT NEXTVAL('COLLECTION_ITEMS_ID_SEQ')
+insert.collection.item = INSERT INTO collection_items (id, collection_id, artifact_id) VALUES (?, ?, ?)
+artifact.id.nextval = SELECT NEXTVAL('ARTIFACTS_ID_SEQ')
+insert.artifact = INSERT INTO artifacts (id, gid, state, creation) VALUES (?, ?, ?, ?)
+artifact.data.id.nextval = SELECT NEXTVAL('ARTIFACT_DATA_ID_SEQ')
+insert.artifact.data = INSERT INTO artifact_data (id, artifact_id, kind, k, v) VALUES (?, ?, ?, ?, ?)
+out.id.nextval = SELECT NEXTVAL('OUTS_ID_SEQ')
+insert.out = INSERT INTO outs (id, artifact_id, name, description, out_type) VALUES (?, ?, ?, ?, ?)
+facet.id.nextval = SELECT NEXTVAL('FACETS_ID_SEQ')
+insert.facet = INSERT INTO facets (id, out_id, name, num, state, description) VALUES (?, ?, ?, ?, ?, ?)
+
+update.collection.name = UPDATE collections SET name = ? WHERE gid = ?
+delete.artifact.from.collection = DELETE FROM collection_items WHERE collection_id = ? AND artifact_id = ?
+delete.collection.by.gid = DELETE FROM collections WHERE gid = ?
+delete.user.by.gid = DELETE FROM user WHERE gid = ?
+delete.artifact.data.by.artifact.id = DELETE FROM artifact_data WHERE artifact_id = ?
+delete.outs.by.artifact.id = DELETE FROM outs WHERE artifact_id = ?
+delete.facets.by.artifact.id = DELETE FROM facets WHERE out_id IN (SELECT id FROM outs WHERE artifact_id = ?)
+
+delete.artifact.by.gid = DELETE FROM artifacts WHERE gid = ?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/datacage-sql/org-postgresql-driver.properties	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,29 @@
+delete.all.users = DELETE FROM users
+delete.all.artifacts = DELETE FROM artifacts
+user.id.nextval = SELECT NEXTVAL('USERS_ID_SEQ')
+user.by.gid = SELECT id FROM users WHERE gid = ?::uuid
+insert.user = INSERT INTO users (id, gid) VALUES (?, ?::uuid)
+collection.by.gid = SELECT id FROM collections WHERE gid = ?::uuid
+collection.id.nextval = SELECT NEXTVAL('COLLECTIONS_ID_SEQ')
+insert.collection = INSERT INTO collections (id, gid, user_id, name, creation) VALUES (?, ?::uuid, ?, ?, ?)
+artifact.by.gid = SELECT id FROM artifacts WHERE gid = ?::uuid
+collection.item.id.nextval = SELECT NEXTVAL('COLLECTION_ITEMS_ID_SEQ')
+insert.collection.item = INSERT INTO collection_items (id, collection_id, artifact_id) VALUES (?, ?, ?)
+artifact.id.nextval = SELECT NEXTVAL('ARTIFACTS_ID_SEQ')
+insert.artifact = INSERT INTO artifacts (id, gid, state, creation) VALUES (?, ?::uuid, ?, ?)
+artifact.data.id.nextval = SELECT NEXTVAL('ARTIFACT_DATA_ID_SEQ')
+insert.artifact.data = INSERT INTO artifact_data (id, artifact_id, kind, k, v) VALUES (?, ?, ?, ?, ?)
+out.id.nextval = SELECT NEXTVAL('OUTS_ID_SEQ')
+insert.out = INSERT INTO outs (id, artifact_id, name, description, out_type) VALUES (?, ?, ?, ?, ?)
+facet.id.nextval = SELECT NEXTVAL('FACETS_ID_SEQ')
+insert.facet = INSERT INTO facets (id, out_id, name, num, state, description) VALUES (?, ?, ?, ?, ?, ?)
+
+update.collection.name = UPDATE collections SET name = ? WHERE gid = ?::uuid
+delete.artifact.from.collection = DELETE FROM collection_items WHERE collection_id = ? AND artifact_id = ?
+delete.collection.by.gid = DELETE FROM collections WHERE gid = ?::uuid
+delete.user.by.gid = DELETE FROM user WHERE gid = ?::uuid
+delete.artifact.data.by.artifact.id = DELETE FROM artifact_data WHERE artifact_id = ?
+delete.outs.by.artifact.id = DELETE FROM outs WHERE artifact_id = ?
+delete.facets.by.artifact.id = DELETE FROM facets WHERE out_id IN (SELECT id FROM outs WHERE artifact_id = ?)
+
+delete.artifact.by.gid = DELETE FROM artifacts WHERE gid = ?::uuid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/messages.properties	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,94 @@
+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
+state.winfo.distance = Choose the range
+state.winfo.uesk.wsp = Choose the waterlevel
+state.winfo.uesk.dgm = Digital Terrain Model
+state.winfo.uesk.profiles = Interpolated Profiles
+state.winfo.uesk.floodplain = Lateral Boundary
+state.winfo.uesk.differences = Differenzen between waterlevel and terrain
+state.winfo.uesk.scenario = Flood Plain / Scenario
+state.winfo.waterlevel_pair_select = Chosen differences
+
+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...)
+calc.w.differences = Differenzen
+
+cross_section = Cross Section
+
+scenario.current = Current
+scenario.potentiel = Potentiel
+scenario.scenario = Scenario
+floodplain.option = Use Floodplain?
+
+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 = {0}-km
+chart.longitudinal.section.yaxis.label = W [{0}]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.longitudinal.annotations.label = {0}.km
+
+chart.cross_section.title = Cross Section for river {0}
+chart.cross_section.subtitle = {0}-km: {1,number,#.###}
+chart.cross_section.xaxis.label = Distance [m]
+chart.cross_section.yaxis.label = W [NN + m]
+
+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}
+
+chart.w_differences.title = Differences
+chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.yaxis.label = m
+chart.w_differences.yaxis.second.label = W [NN + m]
+
+facet.longitudinal_section.annotations = POIs
+facet.discharge_curves.mainvalues.q = Q (main values)
+facet.discharge_curves.mainvalues.w = W (main values)
+
+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]
+export.discharge.curve.at.header = Computed Discharge Curve for {0} {0}-km: {1}
+
+floodmap.wmsbackground = Background Map
+floodmap.riveraxis = River Axis
+floodmap.uesk = Floodmap
+floodmap.barriers = Digitized Objects
+floodmap.kms = Kilometrage
+
+wsplgen.job.queued = WSPLGEN job in queue.
+wsplgen.job.error = An unexpected error while starting WSPLGEN occured.
+
+wsp.selected.string = {0}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/messages_de.properties	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,94 @@
+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
+state.winfo.distance = Wahl der Berechnungsstrecke
+state.winfo.uesk.wsp = Wahl der Wasserspiegellage
+state.winfo.uesk.dgm = Digitales Gel\u00e4ndemodell
+state.winfo.uesk.profiles = Interpolierte Profile
+state.winfo.uesk.floodplain = Laterale Begrenzung
+state.winfo.uesk.differences = Differenzen zwischen Wasserspiegellage und Gel\u00e4nde
+state.winfo.uesk.scenario = \u00dcberschwemmungsfl\u00e4che / Szenario
+state.winfo.waterlevel_pair_select = Ausgew\u00e4hlte Differenzen
+
+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
+calc.w.differences = Differenzen
+
+cross_section = Querprofil
+
+scenario.current = Aktuell
+scenario.potentiel = Potentiell
+scenario.scenario = Szenario
+
+floodplain.option = Talaue verwenden?
+
+river = Fluss
+calculation_mode = Berechnungsart
+ld_locations = Ort(e)
+
+chart.cross_section.title = Querprofildiagram für Gew\u00e4sser {0}
+chart.cross_section.subtitle = {0}-km: {1,number,#.###}
+chart.cross_section.xaxis.label = Abstand [m]
+chart.cross_section.yaxis.label = W [NN + m]
+
+chart.longitudinal.section.title = W-L\u00e4ngsschnitt
+chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = {0}-km
+chart.longitudinal.section.yaxis.label = W [{0}]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.longitudinal.annotations.label = {0}.km
+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}
+
+chart.w_differences.title = Differenzen
+chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.yaxis.label = m
+chart.w_differences.yaxis.second.label = W [NN + m]
+
+facet.longitudinal_section.annotations = Streckenfavoriten
+facet.discharge_curves.mainvalues.q = Q (Haupt- und Extremwerte)
+facet.discharge_curves.mainvalues.w = W (Haupt- und Extremwerte)
+
+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]
+export.discharge.curve.at.header = Berechnete Abflusskurve f\u00fcr {0} {0}-km: {1}
+
+floodmap.wmsbackground = Hintergrundkarte
+floodmap.riveraxis = Flussachse
+floodmap.uesk = \u00dcberschwemmungsfl\u00e4che
+floodmap.barriers = Digitalisierte Objekte
+floodmap.kms = Kilometrierung
+
+wsplgen.job.queued = WSPLGEN Berechnung befindet sich in Warteschlange.
+wsplgen.job.error = Ein unerwarteter Fehler beim Starten von WSPLGEN ist aufgetreten.
+
+wsp.selected.string = {0}
--- /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:51 2012 +0200
@@ -0,0 +1,94 @@
+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
+state.winfo.distance = Wahl der Berechnungsstrecke
+state.winfo.uesk.wsp = Wahl der Wasserspiegellage
+state.winfo.uesk.dgm = Digitales Gel\u00e4ndemodell
+state.winfo.uesk.profiles = Interpolierte Profile
+state.winfo.uesk.floodplain = Laterale Begrenzung
+state.winfo.uesk.differences = Differenzen zwischen Wasserspiegellage und Gel\u00e4nde
+state.winfo.uesk.scenario = \u00dcberschwemmungsfl\u00e4che / Szenario
+state.winfo.waterlevel_pair_select = Ausgew\u00e4hlte Differenzen
+
+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
+calc.w.differences = Differenzen
+
+cross_section = Querprofil
+
+scenario.current = Aktuell
+scenario.potentiel = Potentiell
+scenario.scenario = Szenario
+
+floodplain.option = Talaue verwenden?
+
+river = Fluss
+calculation_mode = Berechnungsart
+ld_locations = Ort(e)
+
+chart.cross_section.title = Querprofildiagramm für Gew\u00e4sser {0}
+chart.cross_section.subtitle = {0}-km: {1,number,#.###}
+chart.cross_section.xaxis.label = Abstand [m]
+chart.cross_section.yaxis.label = W [NN + m]
+
+chart.longitudinal.section.title = W-L\u00e4ngsschnitt
+chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = {0}-km
+chart.longitudinal.section.yaxis.label = W [{0}]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.longitudinal.annotations.label = {0}.km
+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}
+
+chart.w_differences.title = Differenzen
+chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.yaxis.label = m
+chart.w_differences.yaxis.second.label = W [NN + m]
+
+facet.longitudinal_section.annotations = Streckenfavoriten
+facet.discharge_curves.mainvalues.q = Q (Haupt- und Extremwerte)
+facet.discharge_curves.mainvalues.w = W (Haupt- und Extremwerte)
+
+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]
+export.discharge.curve.at.header = Berechnete Abflusskurve f\u00fcr {0} {0}-km: {1}
+
+floodmap.wmsbackground = Hintergrundkarte
+floodmap.riveraxis = Flussachse
+floodmap.uesk = \u00dcberschwemmungsfl\u00e4che
+floodmap.barriers = Digitalisierte Objekte
+floodmap.kms = Kilometrierung
+
+wsplgen.job.queued = WSPLGEN Berechnung befindet sich in Warteschlange.
+wsplgen.job.error = Ein unerwarteter Fehler beim Starten von WSPLGEN ist aufgetreten.
+
+wsp.selected.string = {0}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/messages_en.properties	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,92 @@
+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
+state.winfo.distance = Choose the range
+state.winfo.uesk.wsp = Choose the waterlevel
+state.winfo.uesk.dgm = Digital Terrain Model
+state.winfo.uesk.profiles = Interpolated Profiles
+state.winfo.uesk.floodplain = Lateral Boundary
+state.winfo.uesk.differences = Differences between waterlevel and terrain
+state.winfo.uesk.scenario = Flood Plain / Scenario
+state.winfo.waterlevel_pair_select = Chosen Differences
+
+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...)
+calc.w.differences = Differences
+
+cross_section = Cross Section
+
+scenario.current = Current
+scenario.potentiel = Potentiel
+scenario.scenario = Scenario
+
+floodplain.option = Use Floodplain?
+
+river = River
+calculation_mode = Calculation Mode
+ld_locations = Location(s)
+
+chart.cross_section.title = Cross Section for river {0}
+chart.cross_section.subtitle = {0}-km: {1,number,#.###}
+chart.cross_section.xaxis.label = Distance [m]
+chart.cross_section.yaxis.label = W [NN + m]
+
+chart.longitudinal.section.title = W-Longitudinal Section
+chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = {0}-km
+chart.longitudinal.section.yaxis.label = W [{0}]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.longitudinal.annotations.label = {0}.km
+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}
+
+chart.w_differences.title = Differences
+chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.yaxis.label = m
+chart.w_differences.yaxis.second.label = W [NN + m]
+
+facet.longitudinal_section.annotations = POIs
+
+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]
+export.discharge.curve.at.header = Computed Discharge Curve for {0} {0}-km: {1}
+
+floodmap.wmsbackground = Background Map
+floodmap.riveraxis = River Axis
+floodmap.uesk = Floodmap
+floodmap.barriers = Digitized Objects
+floodmap.kms = Kilometrage
+
+wsplgen.job.queued = WSPLGEN job in queue.
+wsplgen.job.error = An unexpected error while starting WSPLGEN occured.
+
+wsp.selected.string = {0}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/.classpath	Fri Sep 28 12:14:51 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:51 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:51 2012 +0200
@@ -0,0 +1,6387 @@
+2011-11-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Set value "distance" for attribute "ld_mode".
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java
+	  Set value "locations" for attribute "ld_mode".
+
+2011-11-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties:
+	  Bugfix: Added a missing string that prevented packaging the client.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties:
+	  Synced with FLYSConstants.properties.
+
+
+2011-11-04	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added translations for datacage-element- strings.
+
+	* 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,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties:
+	  Added tranlations, corrected one.
+
+2011-11-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Added methods to add and remove Layer instance to/from Map and grant
+	  public access to createWMSLayer() method.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: Moved
+	  code to add new FacetRecords to the grid to an own method
+	  addFacetRecord().
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java:
+	  Override addFacetRecord() to be able to detect new FacetRecords. In this
+	  case, we gonna create new WMS layers based on the FacetRecord and add a
+	  new Layer object to the Map.
+	  Override createRemoveItem() which removes a layer from Map and from
+	  ThemePanel.
+
+2011-11-01  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Updated the theme editor window layout.
+
+2011-11-01  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue 400.
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Iterate over a list of all theme settings to display all available
+	  settings.
+
+2011-11-01	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Cosmetics, Refactoring.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java
+	  (createSpinnerHandler): Extracetd, to improve readability.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java
+	  (requestRedraw): Made final to be accessible from extracted handler
+	   construction in CrossSectionChartThemePanel.
+
+2011-11-01	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix flys/issue306 (CrossSections: Remove functionality from table
+	header).
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java:
+	  Disable context menu on listgrid header.
+
+2011-10-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java:
+	  Override getSingleContextMenu() to add a "zoom to layer" menu item which
+	  is specific to the map. This menu item is displayed if the specific theme
+	  has an "extent" attribute set. If the item is clicked, the
+	  LayerZoomCallback is called.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Implements the LayerZoomCallback to zoom to the extent of a specific
+	  layer.
+
+	* 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 for the "zoom to layer" menu item.
+
+2011-10-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/MapHelper.java: New. It
+	  currently provides a method that parses a MapConfig from Document.
+
+	* src/main/java/de/intevation/flys/client/shared/model/MapConfig.java: New.
+	  This object saves values in a HashMap. Some basic information like srid,
+	  max extent and initial extent can be set/get via methods.
+
+	* src/main/java/de/intevation/flys/client/client/services/MapOutputServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MapOutputService.java,
+	  src/main/java/de/intevation/flys/client/server/MapOutputServiceImpl.java:
+	  This service now returns an instance of MapConfig that contains some basic
+	  information required by OpenLayers to create maps.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  OpenLayers maps are created using the MapConfig returned by
+	  MapOutputService but the layers added to the map are taken from
+	  MapThemePanel to keep the ThemePanel and the Map in sync.
+
+	* 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_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: Added
+	  error strings for the case that no map config was retrieved in
+	  MapOutputService.
+
+2011-10-26	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Let datacage-window include the "static" part. Note that now many
+	  options might be shown with which interaction might not lead to the
+	  epected results.
+
+2011-10-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/BaseServlet.java: Added a
+	  an environment variable FLYS_CLIENT_LOG4J_PROPERIES that might be used
+	  to use an alternative log4j.properties instead of the default one which
+	  comes with the SVN. This variable needs to point to a properties file.
+
+2011-10-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* .: Modified svn:ignore property to ignore log4j.properties.
+
+2011-10-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/webapp/WEB-INF/web.xml: Added a context parameter that might be
+	  used to determine the URL of the Artifact server.
+
+	* src/main/java/de/intevation/flys/client/client/services/MapOutputServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MapOutputService.java,
+	  src/main/java/de/intevation/flys/client/server/MapOutputServiceImpl.java:
+	  Removed the URL parameter from doOut(). This parameter is fetched from
+	  ServletContext.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Modified the call of MapOutputService.doOut().
+
+2011-10-25  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue 118/203
+
+	* src/main/java/de/intevation/flys/client/client/ui/wq/QDTable.java:
+	  Added attribute to lock the table if the input rows are hidden.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Ignore clicks if the table is locked and clear values on input mehtod
+	  change.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  Clear the input values if the input array length is 0.
+
+	* 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_de.properties:
+	  Updated i18n strings.
+
+2011-10-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/LoadArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/MapOutputServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/FeedServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/meta/Converter.java,
+	  src/main/java/de/intevation/flys/client/server/SetCollectionNameServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/UserCollectionsServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/UserServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CollectionAttributeServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ExportServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/MapInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/SetCollectionTTLServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/GetArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DescribeArtifactServiceImpl.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/WQInfoServiceImpl.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/FLYSArtifactCreator.java,
+	  src/main/java/de/intevation/flys/client/server/DoCollectionAction.java,
+	  src/main/java/de/intevation/flys/client/server/ReportServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/RemoveArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DeleteCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ChartServiceHelper.java,
+	  src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DistanceInfoXML.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java,
+	  src/main/java/de/intevation/flys/client/server/RiverServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CollectionHelper.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactHelper.java,
+	  src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Replaced logging to stderr and stdout with log4j loggers.
+
+2011-10-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/BaseServlet.java: New.
+	  This HttpServlet is used to setup some config stuff like log4j
+	  configuration.
+
+	* src/main/webapp/WEB-INF/log4j.properties: A Log4J configuration file.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the BaseServlet which is
+	  loaded initially to setup config stuff.
+
+	* pom.xml: Added Apache Log4J dependency.
+
+2011-10-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/MapOutputServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MapOutputService.java,
+	  src/main/java/de/intevation/flys/client/server/MapOutputServiceImpl.java:
+	  New (but not finished yet). This service calls the out() REST resource
+	  of an ArtifactCollection and should (work in progress) return a map
+	  configuration which will be used to initialize an OpenLayers map.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Call the MapOutputService while instantiating a new MapOutputTab but
+	  there is no code that handles the response yet (because the service
+	  currently doesn't return a value).
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new MapOutputService.
+
+2011-10-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/Toolbar.java: New. An
+	  abstract toolbar that acts as parent for ChartToolbar and MapToolbar.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  Subclass Toolbar which now makes the datacage stuff.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Adjusted the constructor call of OutputTab which now takes an instance
+	  of CollectionView.
+
+	* src/main/java/de/intevation/flys/client/client/ui/OutputTab.java: Stores
+	  an instance of CollectionView and implements methods to retrieve the
+	  current user and artifact.
+
+2011-10-24	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix flys/issue390 (W-Diff: Icon zum Löschen anpassen).
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Polishing and use different icon to remove rows in selections
+	  listgrid.
+
+2011-10-24	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Replaced icons white by a transparent background.
+
+	* src/main/webapp/images/removeFeature.png: Added transparency to
+	  white background.
+
+2011-10-21  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue352 (W-INFO / WSP: Markierung bei Verschieben in Steuerung geht verloren)
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Keep grid selection alive after theme has been modified.
+
+2011-10-21  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Renamed 'textbackground' to 'backgroundcolor'.
+
+2011-10-20  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Display new attributes in editor window.
+
+2011-10-20  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Moved the context menu handler to constructor to have the menu
+	  available in each subclass.
+
+2011-10-20  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Reordered the attribute list.
+
+2011-10-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ExportPanel.java: Removed
+	  superfluous imports.
+
+2011-10-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Modified the visual result of createOld() which will now display each W
+	  or Q in a single row.
+
+2011-10-19  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Group the theme attributes.
+
+2011-10-19  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Convert all color attributes from html to rgb notation.
+
+2011-10-19	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added a displayName propertie to Recommendations and add names to data
+	String such that they survive a "backjump" (reparameterization).
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java,
+	  src/main/java/de/intevation/flys/client/shared/model/ToLoad.java:
+	  Keep displayNames for recommendations.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Recommendation.java:
+	  Added displayName, getter and setter.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Set displayName for create recommendations, add it to Data String.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java,
+	  src/main/java/de/intevation/flys/client/client/ui/RecommendationPairRecord.java:
+	  Do not store name separately from Recommendation
+
+2011-10-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartServiceHelper.java:
+	  Add the format string into the xml document for querying charts.
+
+2011-10-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Removed unused string which has been commited by mistake.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Make "zus" and flood protections usable for W-Differences (with yet
+	many constraints, e.g. server will fail if flood protection is
+	subtrahend).
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Discern static and calculated data by pretty ugly hack, set factory
+	  to "clone" with accordingly.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Request the system ("static") part of datacage in wdiff-input
+	  selection as well.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Enable hack in DatacageTwinPanel, set ids to id content, such that
+	  recommendations and toloads can be created from selected elements in
+	  Datacage.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Recommendation.java:
+	  (setFactory): Allow to change factory during live time.
+
+2011-10-18  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Prepared editor UI for annotation styling.
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Minor cosmetics.
+
+	* src/main/java/de/intevation/flys/client/client/services/MetaDataService.java,
+	  src/main/java/de/intevation/flys/client/client/Config.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacagePanel.java:
+	  Cosmetics (whitespace).
+
+2011-10-18	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/LoadArtifactServiceImpl.java:
+	  Allow to load Artifacts with different factories in one go (as
+	  fallback). Documentation added.
+
+2011-10-18  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Style.java,
+	  src/main/java/de/intevation/flys/client/shared/model/StyleSetting.java,
+	  src/main/java/de/intevation/flys/client/shared/model/CollectionItemAttribute.java,
+	  src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Added documentation.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java:
+	  Added support for chart exports. If the Http parameter "export=true" is
+	  set, the header of the Http request is modified to trigger a download.
+	  Currently, PNG, SVG and PDF export is supported.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Added a method that returns the URL for a chart export.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Added download icons for png, pdf and svg.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSImages.java: Added
+	  images for png/pdf/svg downloads.
+
+	* src/main/java/de/intevation/flys/client/client/images/downloadPNG.png,
+	  src/main/java/de/intevation/flys/client/client/images/downloadPDF.png,
+	  src/main/java/de/intevation/flys/client/client/images/downloadSVG.png:
+	  New. Icons for png/pdf/svg export of charts.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ImgLink.java: New.
+	  This class creates a link with an image.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ExportPanel.java:
+	  Makes use of ImgLink to create export icons.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Bugfix: adjusted a method call of XMLUtils.ElementCreator.addAttr()
+	  which avoided compiling flys-client.
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Use images for line type and line size selection.
+
+	* src/main/webapp/images/linestyle-dash-10.png,
+	  src/main/webapp/images/linestyle-dash-10-5.png,
+	  src/main/webapp/images/linestyle-1px.png,
+	  src/main/webapp/images/linestyle-2px.png,
+	  src/main/webapp/images/linestyle-3px.png,
+	  src/main/webapp/images/linestyle-4px.png,
+	  src/main/webapp/images/linestyle-5px.png,
+	  src/main/webapp/images/linestyle-dash-20-10.png,
+	  src/main/webapp/images/linestyle-6px.png,
+	  src/main/webapp/images/linestyle-dash-30-10.png,
+	  src/main/webapp/images/linestyle-7px.png,
+	  src/main/webapp/images/linestyle-8px.png,
+	  src/main/webapp/images/linestyle-dash-20-5-15-5.png:
+	  New.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue253 (Diagramm: Mehrfachauswahl zum Verschieben/Löschen auf Themen)
+
+	* 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/AttributedTheme.java:
+	  Prepared Themes to support a "visible" attribute.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Write the "visible" attribute of a Theme into the attribute of a
+	  Collection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: Do
+	  not display Themes which are not visible (getVisible() == 0).
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Added new values for line type.
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Set window position to page center.
+	  Removed parent element.
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Show theme name in static text field.
+
+2011-10-17  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Request redraw on attribute change.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Made request redraw method public and added method to show editor
+	  window.
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Removed collection view, added theme panel. Request redraw on success.
+
+2011-10-17	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/RecommendationPairRecord.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Cosmetics, docs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Cosmetics, docs, also made two methods static.
+
+2011-10-17	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Donate own error for failcase of remove-artifact.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Donated own error code for remove-artifact.
+
+2011-10-17  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue366 (W-INFO / Abflusskurve, Diagramm: Umbenennen von Themen
+	und Anzeige im Diagramm)
+
+	* 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/AttributedTheme.java:
+	  Added a setter method for descriptions.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Description modifications will now be persisted to artifact server.
+
+2011-10-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties:
+	  Added missing resources for datacage.
+
+2011-10-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java:
+	  Set the correct index when saving changed attributes.
+
+2011-10-14  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue288 (ÜSK: Legende hinzufügen)
+
+	* src/main/java/de/intevation/flys/client/shared/MapUtils.java: New.
+	  Helper class for maps.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Set width and min width of MapThemePanel to 300px and allow to resize it.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: Moved
+	  Record and ListGrid creation out to own methods which subclasses can
+	  override.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java:
+	  Override createNewGrid() to add a column for displaying legend items.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added string for the "style" header in the MapThemePanel.
+
+2011-10-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Style.java,
+	  src/main/java/de/intevation/flys/client/shared/model/CollectionItemAttribute.java,
+	  src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Use the theme index to identify the correct style.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java:
+	  Set the style index.
+
+2011-10-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Removed top level '${datacage}' using CHILDREN mode for tree.
+	  Introduced i18n.
+	  If tree contains less the MAX_OPEN (30) nodes and leafs the
+	  tree is rendered all open. This minimizes clicks.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties:
+	  i18n for data cage
+
+2011-10-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/webapp/images/adjustElevation.png: Replaced old one that had a
+	  gray background with a new one with transparent background.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added error message(i18n) for fail case of removeArtifactService.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added error_remove_artifact .
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Resolved TODO by employing "own" error message (removeArtifact).
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Removed obsolete imports.
+
+	* src/main/java/de/intevation/flys/client/client/services/RemoveArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DigitizePanel.java:
+	  Removed obsolete imports.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	In W-Diff selection GUI, track which items have been removed from the
+	listgrid, remove corresponding artifacts from collection if they are
+	not needed anymore using the new RemoveArtifactService.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Track removed items from ListGrid until clicking on "proceed". Then,
+	  check whether artifacts removed from the list are still needed to
+	  calculate other difference (which are still in the ListGrid), remove
+	  them otherwise.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added helper in use of new RemoveArtifactService.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java 
+	  (removeArtifact): New method to remove an artifact from a
+	  collection.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added implementation and configuration of RemoveArtifactService.
+
+	* src/main/java/de/intevation/flys/client/server/RemoveArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/RemoveArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/RemoveArtifactService.java:
+	  New files with implementation of RemoveArtifactService.
+
+	* src/main/webapp/WEB-INF/web.xml: Register new servlet/service.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactHelper.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Cosmetics, doc.
+
+2011-10-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  Moved code to mark features 'selected' to FloodMap. Display warnings if
+	  elevation control is clicked but no feature is selected or more than one
+	  feature is selected.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java: Do
+	  the "select feature" stuff here. Selected features will now have a
+	  fatter stroke.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added warning and error messages used when elevation control is used.
+
+2011-10-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Updated SmartGWT to 2.5
+
+	* pom.xml: Bumped SmartGWT from 2.4 to 2.5
+
+	* src/main/java/de/intevation/flys/client/FLYS.gwt.xml:
+	  Using Enterprise instead of EnterpriseBlue theme.
+
+2011-10-13  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue297 (ÜSK: Eingabe von Höhen ermöglichen)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java:
+	  Made those classes more robust while setting Styles for features without
+	  barrier type.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java:
+	  Moved the constant to mark features to FloodMap. Added a new control to
+	  adjust the elevation values of features.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/ElevationWindow.java:
+	  New. This Window displays a grid that contains the vertices of the
+	  specified vector feature. Users are able to modify the elevation value
+	  for each vertex in the grid.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  New strings used for the elevation control button/icon.
+
+	* src/main/webapp/images/adjustElevation.png: A new icon used for the
+	  control to adjust the elevation values of features in the map.
+
+2011-10-12  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue331 (ÃœSK: OpenLayers innerhalb der eigenen Distribution bereitstellen)
+
+	* src/main/webapp/FLYS.html: Fetch OpenLayers-2.11 lib from local server
+	  instead from remote repository.
+
+2011-10-12  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue293 (ÜSK: Legende hinzufügen)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java:
+	  Added icons to the barrier combobox.
+
+	* src/main/webapp/images/dam.png,
+	  src/main/webapp/images/pipe1.png,
+	  src/main/webapp/images/pipe2.png,
+	  src/main/webapp/images/ditch.png,
+	  src/main/webapp/images/ring_dike.png: New icons used in the barrier
+	  combobox in the digitize panel.
+
+2011-10-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java:
+	  Throw a ServerException if no styles were found in the XML document.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Display a warning dialog if no style for the selected theme was found.
+
+	* 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.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.properties:
+	  New error messages that are displayed if no style was found for a given
+	  theme.
+
+2011-10-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: Moved
+	  the ContextMenu creation from ChartThemePanel to ThemePanel - we want a
+	  ContextMenu in the ChartThemePanel as well. In addition, there are new
+	  menu items to activate/deactivate and remove themes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/StyleEditorWindow.java,
+	  src/main/java/de/intevation/flys/client/client/ui/StyleEditorWindow.java:
+	  Moved the StyleEditorWindow to an upper package, because it will also be
+	  used in the map.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added new strings for context menu items of the ThemePanel.
+
+2011-10-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Validate selected pairs for differences, show errors if invalid.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java
+	  (onClick, validate): Validate and show errors if data invalid.
+	  Cosmetics, removed junk and resolved respective todo, documentation.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  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_de.properties:
+	  Added error messages.
+
+2011-10-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Show only "W"s in input helper for W-Diffs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Request (datacage-specific!) "waterlevels"-out instead of more
+	  general "longitudinal_section"-out.
+
+2011-10-12  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Partial fix for flys/issue304 [1] (Erweiterte Funktionen W-Differenzen),
+	enable multiple clones of same recommendation, but be 'sparse'.
+
+	* src/main/java/de/intevation/flys/client/server/LoadArtifactServiceImpl.java
+	  (loadMany): While iterating over recommendations and creating
+	  clones, keep track of results, do not clone a second time if same
+	  recommendation is present multiple times in list, use "old" clone
+	  instead. Throw excpetion in fail-case.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Use list instead of set, 'sparseness' is now achieved by
+	  LoadArtifactService. Docs, removed junk.
+
+2011-10-11  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue300 (ÜSK: Reihenfolge der Ebenen findet keine Berücksichtigung)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Implemented a ThemeMovedCallback to synchronize the ThemePanel's layer
+	  order with the internal order of the Map.
+
+2011-10-11  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Squashed "swapped minuend and subtrahend"-bug in wdiff calculation.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Use LinkedHashSet instead of HashSet to keep the given order.
+	  Removed some debug statements.
+
+2011-10-11  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	Enable reparameterization of WINFO-Artifacts in WDiff-branch. Do not
+	reclone in parameterization.
+
+	* src/main/java/de/intevation/flys/client/client/ui/RecommendationPairRecord.java:
+	  Add flag to remember whether already cloned.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Recreate already cloned pairs of Ws from datastring, do not reclone
+	  these.
+
+2011-10-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: Added
+	  a method fireThemeMoved which is called after a Theme in this panel is
+	  moved.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java:
+	  Added in internal interface ThemeMovedCallback which might be used to
+	  listen to theme move events. Therefore, MapThemePanel overrides the
+	  fireThemeMoved method and calls onThemeMoved() on ThemeMovedCallback if
+	  it exists.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/webapp/FLYS.html: Import OpenLayers 2.11.
+
+	* pom.xml: Upgrade of GWT-OpenLayers from 0.5 to 0.6
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Removed obsolete imports.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue289 (ÜSK: Messen von Strecken und Flächen in falscher Dimension)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MeasureControl.java:
+	  Measured areas are now displayed in 'ha'.
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/RecommendationPairRecord.java:
+	  Polish with doc, removed junk.
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/RecommendationPairRecord.java:
+	  New file. Extracted from DatacagePairWidget.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Extracted RecommendationPairRecord implementation and constats.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Improved displayed strings for selections of pairs. First steps
+	  towards the ability to jump back ("reparameterize") the artifact.
+	  Adjusted to RecommendationPairRecord-extraction.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue375 (ÃœSK, Streckemessung)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MeasureControl.java:
+	  Added a formatter for lines and areas which is used to limit the number
+	  of fractions.
+
+2011-10-10  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TextProvider.java:
+	  Cosmetics (docs and whitespace).
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Style.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionItemAttributeService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionItemAttributeServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Removed superfluous imports.
+
+2011-10-10  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue326 (Projektliste: Projekte ohne Parametrisierung werden angezeigt, können aber nicht geöffnet werden.)
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Take care on
+	  empty projects while opening existing projects. Fetch the master
+	  Artifact only if the Collection has an Artifact set.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Enabled CollectionView creation without Artifact but existing
+	  Collection.
+
+2011-10-07  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue233 (W-Info: Dauerlinienberechnung / "Lädt Daten")
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  Force a redraw of the computation data grid if no data could be
+	  retrieved from server - this displays an error message.
+
+2011-10-05  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Edited item attributes are saved and send to the server on accept.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionItemAttributeService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionItemAttributeServiceAsync.java:
+	  Added service method to save new item attributes.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Style.java:
+	  Added facet to style attributes.
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionItemAttribute.java:
+	  Added artifact uuid to collection item attributes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/StyleEditorWindow.java:
+	  Added item changed handler to save new values and update the
+	  serverside values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Set the selected facet record to the editor window.
+
+2011-10-05  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/StyleEditorWindow.java:
+	  New methods for color transformation (html to rgb; rgb to html).
+
+2011-10-04  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/StyleEditorWindow.java:
+	  Build up the editor window from CollectionItemAttribute response.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Style.java:
+	  Added getter for number of settings and settings based on the index.
+
+2011-10-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  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_de.properties:
+	  Added an error message that might be used if no calculation result was
+	  found for calculation result panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  Improved error handling if no calculation result was found - display
+	  error message in this case.
+
+2011-09-30  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue357 (W-Info / Wasserspiegellage, Diagramm)
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Removed header title of selection column.
+
+2011-09-30  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Added skeleton for editor window.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/StyleEditorWindow.java:
+	  New. Skeleton for editor window.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Set the collection view as attribute to have a parent for the
+	  style editor window.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added strings for the style editor window.
+
+2011-09-30  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Added context menu to themes list.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added string for context menu entry.
+
+2011-09-30  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionItemAttributeServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Style.java,
+	  src/main/java/de/intevation/flys/client/shared/model/StyleSetting.java,
+	  src/main/java/de/intevation/flys/client/shared/model/CollectionItemAttribute.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionItemAttributeService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionItemAttributeServiceAsync.java:
+	  New. Added new service and model for collection item attributes.
+
+	* src/main/webapp/WEB-INF/web.xml:
+	  Added the collection item attribute service.
+
+2011-09-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Fix accidentally messed-up indentation.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Fix indentation.
+
+2011-09-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Neaten displayed gridcontent when selecting waterlevels for wdiff,
+	do not allow multi-selection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Display facets name in table when selecting pairs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  (getSelectionTitle): New function to access visible titles of
+	   selection.
+
+2011-09-29  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue179 (Zoom Out funktioniert nichtzuverlässig)
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Repaired broken zoom-out function.
+
+2011-09-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Re-added the button to zoom-out by a given factor. Added a new one to
+	  step back to the last extent.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Re-added the function to zoom-out by a given factor.
+
+	* 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_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: Added
+	  the path to the zoom-back icon.
+
+	* src/main/webapp/images/zoom-back.png: New. Icon for the zoom-back
+	  button.
+
+2011-09-29  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue198 (Diagramm: Zu vorheriger Zoomstufe per Klick zurückkehren)
+
+	* src/main/java/de/intevation/flys/client/shared/model/ZoomObj.java: New.
+	  This object is used to save the zoom extent of charts.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  The zoom-out button calls ChartOutputTab.zoomOut() without a integer
+	  value.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  The Zoom-Out action no longer zooms out for a specific factor, but to the
+	  last extent. The current extent of a chart is pushed onto a stack before
+	  a Zoom-In action takes place. Zoom-out then zooms to the last extent
+	  retrieved from the stack.
+
+2011-09-29  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue336 (W-INFO / Berechnung Wasserstand an Orten, Zurodnung Bezugspegel)
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java:
+	  Added two new methods getRiver() (returns the selected river) and
+	  getKMRange() (returns the selected min/max KM values).
+
+	* src/main/java/de/intevation/flys/client/shared/DoubleUtils.java: New.
+	  Utility class for working with double values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Moved the code to determine the min/max KM range and river out to
+	  ArtifactDescription. There, it is available for other classes as well.
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue328 (W-INFO / ÜSK: Auswahl der Wasserspiegellage / Auswahlunterstützung)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WspDatacagePanel.java:
+	  Fetch datacage out 'waterlevels' only (possible since last rev in
+	  flys-artifacts). The datacage will provide W facets only - no Q facets
+	  are displayed.
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue211 (W-INFO: Wasserspiegellagenberechnung / Auswahlunterstützung Abfluss)
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Reset values of km range mode if single km mode is selected and the
+	  other way arround.
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue119 (W-INFO: Wasserspiegellagenberechnung / Dateneingabe ausserhalb des Wertebereichs)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Display min/max values when input validation for ranges failed.
+
+2011-09-28  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue111 (W-INFO: Wasserspiegellagenberechnung / Strecke)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Reset the values of range mode if single mode is selected and the other
+	  way arround.
+
+2011-09-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Make rows of in list with differences (WDiff) removable.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Make rows of in list with differences (WDiff) removable.
+
+2011-09-27	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Use HashSet to avoid duplicate clones in Collections with WDiffs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Use HashSet instead of list to avoid duplicate clones, cosmetics.
+
+2011-09-27	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Cosmetics, documentation.
+
+	* src/main/java/de/intevation/flys/client/server/LoadArtifactServiceImpl.java
+	  (loadMany): Documented, added TODO.
+
+2011-09-27	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Implement equals and hashCode for Recommendation, Recommendation.Facet
+	and Recommendation.Filter .
+
+	* src/main/java/de/intevation/flys/client/shared/model/Recommendation.java
+	  (equals, hashCode, Filter.equals, Filter.hashCode, Facet.equals)
+	  (Facet.hashCode): New.
+
+2011-09-27  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue321 (ÃœSK: Aktualisieren der Parameterliste nach Beendigung der Berechnung)
+
+	* src/main/java/de/intevation/flys/client/client/ui/LoadingPanel.java: Put
+	  the updated Artifact into the CollectionView when the calculation is
+	  finished.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Update the UI after the Artifact of this CollectionView is updated.
+
+2011-09-26  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue298 (Karte: Automatischer Zoom auf Berechnungsergebnisse)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  The initial zoom of the map is determined by the extents of all layers
+	  in the map OR by the wsplgen layer, if such layer is existing and if it
+	  has an extent set.
+
+2011-09-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Added new method to adjust the selection type (multi or single select).
+
+	* src/main/java/de/intevation/flys/client/client/ui/DemDatacagePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WspDatacagePanel.java:
+	  Set single selection type for DatacageWidget's grid - only one selected
+	  item is allowed here.
+
+2011-09-26  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue323 (Datenkorb: Doppelklick im Datenkorb sollte gewähltes Thema (Themen) laden)
+
+	* src/main/java/de/intevation/flys/client/client/event/DatacageDoubleClickHandler.java:
+	  New. Interface for double click handlers used in the datacage.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Implements the DatacageDoubleClickHandler. After a doubleClick event has
+	  been received, the selected Themes from datacage widget are loaded and
+	  the window is destroyed.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  A double click on a tree node triggers the onDoubleClick method for each
+	  registered DatacageDoubleClickHandler. A DatacageDoubleClickHandler
+	  may register itself via DatacageWidget.addDatacageDoubleClickHander()
+	  method.
+
+2011-09-21	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+ Clone Subtrahend and Minuend in WDiff-branch of WINFO with waterlevelfactory.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  Clone recommendations with waterlevel-factory, prepare to have a
+	  third column in listgrid to remove pairs.
+
+2011-09-21  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue315 (Überschwemmungsfläche: String bei Streckenauswahl)
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  Little modifications to allow subclassing.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangeOnlyPanel.java:
+	  New. A subclass of DoubleRangePanel that does NOT display a "step"
+	  field.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Set correct title for this panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistanceOnlyPanel.java:
+	  New. A subclass of DistancePanel that instantiates a
+	  DoubleRangeOnlyPanel instead of a DoubleRangePanel. So, there are only
+	  "from" and "to" fields visible. In addition, this panel only returns
+	  Data objects for these two fields.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Registered the DistanceOnlyPanel.
+
+2011-09-19  Hans Plum <hans@intevation.de>
+
+	* NEWS for V 2.5.0:
+	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-09-19  Hans Plum <hans@intevation.de>
+
+	* NEWS for V 2.4.0:
+	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-09-19  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.5
+
+	* Changes: Prepared changes for the release.
+
+2011-09-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TableFilter.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ProjectList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Removed needless imports.
+
+2011-09-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DemDatacagePanel.java:
+	  Display datacage panel with DEMs only. Return Data object with selected
+	  database ID after pushing 'next' button.
+
+2011-09-16  Ingo Weinzierl <ingo@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:
+	  Added a further 'parameters' parameter to be able to adjust the metadata
+	  query more precise.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Added a further 'parameters' attribute and a constructor. The
+	  'parameters' attribute is used for the metadata query as well as the
+	  user and the outs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePanel.java:
+	  The parameters for the metadata query can now be adjusted by overriding
+	  getUser(), getOuts() and getParameters().
+
+	* src/main/java/de/intevation/flys/client/client/ui/DemDatacagePanel.java:
+	  This panel now able to only  display the DEMs in the datacage panel.
+
+2011-09-16  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue265
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Added method to be able to register TabSelecedHandler at the
+	  CollectionView.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DigitizePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java:
+	  Register the panel as TabSelectedHandler to hide or show the barrier
+	  layer in the FloodMap.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Added setter for ParameterList.
+
+2011-09-16	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Updated implementation to a working (but limited and buggy) DatacageTwinPanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  Eat recommendations and add them to list, on "proceed"-click, clone
+	  selected artifacts and add them to selection, collapse list entries
+	  to string and add that string as data.
+
+2011-09-16	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Added a loadMany-Function to LoadArtifactService.
+
+	* src/main/java/de/intevation/flys/client/server/LoadArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/LoadArtifactService.java,
+	  src/main/java/de/intevation/flys/client/client/services/LoadArtifactServiceAsync.java:
+	  Added a loadMany-Function to LoadArtifactService
+
+2011-09-16  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue203
+
+	* src/main/java/de/intevation/flys/client/client/ui/wq/QDTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Rollback for input assistance of "Q/D" table.
+	  The selectable icon columns are visible in  all modes except of
+	  "Q" input mode.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue259 (Daten aus Datenkorb in Diagramm einladen)
+
+	* src/main/java/de/intevation/flys/client/client/event/RedrawRequestEvent.java:
+	  New. This event might be used to define the redraw type. Currently,
+	  there are two types defined: RESET and DEFAULT. DEFAULT means the chart
+	  should only be redrawn, RESET means the chart's state (zoom, etc) should
+	  be reset.
+
+	* src/main/java/de/intevation/flys/client/client/event/RedrawRequestHandler.java:
+	  Added the RedrawRequestEvent to the onRedrawRequest() method.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Implements the HasRedrawRequestHandlers interface. After all Artifacts has
+	  been loaded, the onRedrawRequest() event is fired.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Registered the ChartOutputTab as RedrawRequestHandler.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Distinguishes between the RedrawRequestEvent.Types now. The chart's
+	  state is only reset if the RESET type is given. Otherwise, only the
+	  ThemePanel and the chart image are refreshed.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: Fire
+	  the RedrawRequestEvent with type RESET.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue259 (Daten aus Datenkorb in Diagramm einladen)
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Don't abuse the mechanism to load recommendations any longer. Selected
+	  Facets/Artifacts in this Window are cloned and loaded into the current
+	  Collection using the LoadArtifactService.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProvider.java:
+	  Defined a setter method to inject a Collection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Added a setter/getter method to inject a Collection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Inject the current Collection into the UIProvider and add Artifacts that
+	  have been created to the Collection of CollectionView.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WspDatacagePanel.java:
+	  Clone an Artifact that is selected in the datacage widget and add it to
+	  the current Collection. Finally, return its UUID, the name of the facet
+	  and the index.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added new error messages that will be displayed when an error occured
+	  while cloning an Artifact or adding it to a Collection.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Create a new collection if no Collection is given while initializing a
+	  CollectionView. This is necessary, because a Collection is required in
+	  states that try to add cloned Artifacts (as it is the case in
+	  WspDatacagePanel).
+
+	  !! NOTE: This changes the behaviour - before, Collections have been
+	  created when a Collection has Outputs. Now, a Collection is initially
+	  created when the user starts a new project. !!
+
+2011-09-15  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue199
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Validate than the input field contains a single value.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added i18n string for error message.
+
+2011-09-15  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue200
+
+	* 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/range/LocationsTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/RangeTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Added formatter for numeric columns that converts the decimal
+	  separator.
+
+2011-09-15  Ingo Weinzierl <ingo@intevation.de>
+
+	Refactored the HTTP related code for Collections and Artifacts out to helper
+	classes. This makes us able to combine several Artifact and Collection
+	protocol specific things in new Services. The first one which uses this
+	combination is the LoadArtifactService where a new Artifact is created and
+	finally added to a Collection.
+
+	* src/main/java/de/intevation/flys/client/server/LoadArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/LoadArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/LoadArtifactService.java:
+	  New service to create a new Artifact (with a given factory and a
+	  recommendation) and add it directly to a specified Collection.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java:
+	  Removed HTTP related code - the CollectionHelper should do all this.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java:
+	  Removed HTTP related code - the ArtifactHelper should do all this.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Added HTTP related methods from DescribeCollectionServiceImpl and
+	  AddArtifactServiceImpl.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactHelper.java: New.
+	  This helper class should handle all the HTTP stuff for working with
+	  Artifacts.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new LoadArtifactService.
+
+2011-09-15  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue194
+
+	* src/main/java/de/intevation/flys/client/client/ui/wq/QDTable.java:
+	  Changed table header string and set the column width to percental
+	  value.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added i18n string for "Q/D" table header.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue200
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/wq/QDTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/wq/WTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/LocationsTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/RangeTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Changed columns to the correct type for most tables.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue195
+
+	* 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_de.properties:
+	  Changed i18n string for "Q/D" table header.
+
+2011-09-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DemDatacagePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WspDatacagePanel.java:
+	  New. Two concrete subclasses of DatacagePanel - one for choosing a dem
+	  and one for choosing a waterlevel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePanel.java:
+	  Some refactoring that makes it able to subclass the DatacagePanel. Now,
+	  this class is abstract and needs to be subclassed.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Registered the DemDatacagePanel and WspDatacagePanel as new UIProviders.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue313
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.properties:
+	  Changed i18n string for search field label.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue299
+
+	* src/main/java/de/intevation/flys/client/client/ui/LoadingPanel.java,
+	  src/main/webapp/FLYS.css:
+	  Changed style for loading panel.
+
+2011-09-14	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	New DatacageTwinWidget to be used in W-Differences GUI.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePairWidget.java:
+	  New, display two datacages and a "+" button. On selection and
+	  plus-button press will in future add names of selections into a
+	  given ListGrid.
+
+2011-09-14	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Ease access to selected elements of a DatacageWidget. Make button-less
+	DatacageWidget possible.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Refactored to allow for button-less Widget and still be able to
+	  access current selection as ToLoad-objects from "outside"; in coo
+	  with Ingo Weinzierl.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Fixed a problem with renaming collection view title and duplicating projects.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue166
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Update the collection view title when the project name changes.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue203
+
+	* src/main/java/de/intevation/flys/client/client/ui/wq/QDTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Removed the table listener for "Q" input. The table is no longer used
+	  for input assistance.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue305
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TableFilter.java:
+	  Clear the table filter on tab selection change and input method
+	  change.
+
+2011-09-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/meta/Converter.java:
+	  Extract the name AND the description from dom Element. Use both
+	  attributes to create a new DataCageNode with 'name' and 'description'.
+	  The 'description' depends on the Element: if it has an attribute
+	  'description', this one is used otherwise the 'description' is
+	  "${'name'}".
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataCageNode.java:
+	  Added a new attribute 'description' with getter method and new
+	  constructor.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Display the 'description' of DataCageNodes in the Datacage tree, but use
+	  the 'name' for in the ToLoad object which is created after pressing the
+	  "+" button.
+
+2011-09-14	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Committed rest for fix flys/issue311 (translation of crosssection).
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Use translated "actions" string, use constant instead of spelled-out
+	  string.
+
+2011-09-14	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Fix flys/issue309 (crosssection-spinner does not trigger action when
+	return pressed).
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java:
+	  Trigger ChangedEvent on each keypress when any value given.
+
+2011-09-14  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Fixed NPE problem on empty list.
+	  Changed filter to caseinsensitiv. (Issue285)
+	  Changed the copy-prefix for duplicated projects.
+
+2011-09-14	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Fix flys/issue311 (i18n of crosssection).
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added translation for crosssections "actions" and w-diff export.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java:
+	  Use translated "actions" string.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Use constant instead of spelled-out string.
+
+2011-09-13  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue234/266
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added feature for duplicating projects.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added i18n string for context menu and project name.
+
+2011-09-13  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Issue239
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/LocationsTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/RangeTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Changed field size from fixed to percental value.
+
+2011-09-13  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added filter functionality to the project list.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactFilter.java:
+	  Call default constructor in parameterized constructor to initialize data
+	  structures.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Repaired broken if-statement that avoided adding new DatacageHandlers.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/OutputTab.java: Added
+	  method that returns the name of the current output.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Resolved TODO: the datacage window fetches the data for the current
+	  output now.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue293 (Karte: Farbliche Anpassung der digitalisierten Objekte)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java: Set
+	  style attribute for newly created vector features. The styled is based
+	  on the "type" attribute of a feature. FloodMap.getStyle(String type)
+	  returns the Style for the specified type.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue289 (Karte: Messen von Strecken und Flächen in falscher
+	Dimension)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java: Set
+	  map units 'm'.
+
+2011-09-13  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue286 (Karte: Tooltips für die Werkzeugleiste)
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/MeasureControl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java:
+	  Added tooltips for map controls.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added tooltip texts.
+
+2011-09-12  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  Set the data table cells selectable for copy&paste.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LoadingPanel.java:
+	  Introduced i18n for messages in the loading panel. Note that messages
+	  parsed from Artifact's DESCRIBE are _NOT_ based on the client's locale!
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added strings used in the loading panel.
+
+2011-09-12  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Set header fields to fixed order. (Issue240)
+
+2011-09-12  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/LocationsTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/RangeTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Changed i18n string for empty table and set header fields to fixed order.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added i18n string for empty table.
+
+2011-09-12  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableFilter.java:
+	  The TableFilter implements KeyUpHandler now to receive the last pressed
+	  key.
+	  Removed the 'clear' button and ClickHandler implementation and set the label
+	  text to i18n strings.
+
+	* 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.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added i18n string for filter label.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue282 (Karte: Abstand interpolierte Profile - Default wert)
+
+	* src/main/java/de/intevation/flys/client/client/ui/AutoIntegerPanel.java:
+	  Set default value (if no other value is set) to 'auto'.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Implemented a method getDataValue(String state, String name) that searches
+	  for a specific state and data name and returns the string value if such
+	  a data object exists.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DigitizePanel.java:
+	  Find out the rivername on the fly (was hard coded before).
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataList.java: Made
+	  the DataList cloneable.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DigitizePanel.java:
+	  Remove the data "uesk.barriers" that contains the GeoJSON geometries
+	  from cloned DataList to supress the GeoJSON string in UI.
+
+2011-09-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TableFilter.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Removed needless imports.
+
+2011-09-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/CalculationMessage.java:
+	  New. Status messages and progress information of Artifact's background
+	  processes are stored in here.
+
+	* src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java:
+	  Read status messages and progress information from Artifact's DESCRIBE.
+	  Put a CalculationMessage object into the newly created artifacts.
+
+	* src/main/java/de/intevation/flys/client/shared/model/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifact.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Artifact.java:
+	  Artifacts provide a method getBackgroundMessages() that returns a list
+	  of CalculationMessages.
+
+	* src/main/java/de/intevation/flys/client/client/images/loading.gif: A
+	  spinner used in the loading panel.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSImages.java: Declared
+	  the loading.gif.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LoadingPanel.java: Use
+	  the status message and progress information to fill the loading panel
+	  with text. The 'loading.gif' is placed at the top of this panel for a
+	  nice visual effect.
+
+	* src/main/webapp/FLYS.css: Set some style information for the loading
+	  box.
+
+2011-09-08  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Changed 'addChild()' to 'addMember' at layout container to be able to
+	  remove the content.
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableFilter.java:
+	  Uncommented the fire event line to make the filter work.
+
+2011-09-08	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Minor cosmetics.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Minor cosmetics.
+
+2011-09-08	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Added new UIProvider "DatacageTwinPanel"/ "datacage_twin_panel" (stub). As the
+	Datacage needs to know a user, Modified UIProviderFactory to accept user in
+	calls to getProvider.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageTwinPanel.java:
+	  New. Display two DatacageWidgets.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java
+	  (getProvider): Added "user" argument, create DatacageTwinPanel if asked to.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Pass user argument in call to UIProverFactory.getProvider().
+
+2011-09-08	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Added w_differences translation.
+
+	* 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:
+	  Added "w_differences" translation.
+
+2011-09-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/DescribeArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/DescribeArtifactServiceImpl.java:
+	  New. The task of this service is to fetch the current DESCRIBE document
+	  of a specific Artifact (currently used in the LoadingPanel to determine
+	  the background processing's state).
+
+	* src/main/java/de/intevation/flys/client/client/ui/LoadingPanel.java: The
+	  loading panel is finished when its Artifact has no longer a processing
+	  background task.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the DescribeArtifactService.
+
+2011-09-08	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Cosmetics.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java.
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java.
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java.
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Cosmetics; adjusted and corrected some doc.
+
+
+2011-09-07  Raimund Renkert <raimund.renkert@intevation.de>
+
+	Added a filter for the "description" row of helper input tables.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Added a filter for the input tables.
+	  The filter works as an "as you type" filter, if this is to slow, go to the
+	  'TableFilter' class and change the 'onKeyPress()' handler as documented in
+	  the code.
+
+	* src/main/java/de/intevation/flys/client/client/event/FilterHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/StringFilterEvent.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TableFilter.java:
+	  New. These classes/interfaces are implemented by Ingo and are used for the
+	  table filter.
+	  They provide common functionality and interfaces for filtering.
+
+2011-09-07  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/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifact.java:
+	  An Artifact got a method isInBackground() that returns true, if the
+	  Artifact has been started a background thread in the Artifact-Server -
+	  otherwise it returns false.
+
+	* src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java:
+	  Parse the "background-processing" attribute from Artifact's DESCRIBE
+	  document.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java: Add
+	  a LoadingPanel if the new Artifact that is injected via setArtifact() is
+	  in background mode.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LoadingPanel.java: New.
+	  A window that places itself in front of another component - which disables
+	  user interaction with the parent component. Currently, the loading panel
+	  waits a few seconds an removes itself from parent component.
+
+	* src/main/webapp/FLYS.css: Added a style for the loading panel's label
+	  'loading-message'.
+
+2011-09-07	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Display Action Widget only for cross_section theme/facet.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java:
+	  Put action widget only for cross_section theme, resolving a TODO.
+	  Adjust layout slightly.
+
+2011-09-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java:
+	  Set WSPLGEN specific names for the 'type' attribute after digitizing new
+	  geometries.
+
+2011-09-06	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Fix broken build by changed method name.
+
+2011-09-06	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Subclassed ChartThemePanel, trigger redraws of Chart when interaction with
+	"Action" widgets in ThemePanel occured (but will still crash).
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Extracted CrossSection-specific stuff to own subclass.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java:
+	  New, contains specific parts that were extracted from ChartThemePanel.
+	  After interaction via the "Actions" Widgets (currently only a spinner),
+	  request a chart redraw.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Depending on the outputmode, create a CrossSectionChartThemePanel, register
+	  as redrawRequestHandler.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Implement HasRedrawRequestHandlers interface.
+
+2011-09-06	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Added concept of a RedrawRequestHandler to allow e.g. ChartThemePanel to
+	trigger a redraw of a Chart.
+
+	* src/main/java/de/intevation/flys/client/client/event/HasRedrawRequestHandlers.java,
+	  src/main/java/de/intevation/flys/client/client/event/RedrawRequestHandler.java:
+	  New, trivial yet eventless notification handler interfaces.
+
+2011-09-06  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue166 (Projektname in die Fensterleiste übernehmen)
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Read the name from Collection's DESCRIBE document and create new
+	  Collection objects with this information.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java:
+	  Adapted constructors which now require a name.
+
+	* src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java:
+	  Use the UUID as Collection name after a Collection has been created - at
+	  that time no user defined name is existing.
+
+2011-09-06	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Minor cosmetics.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultDataItem.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifact.java,
+	  src/main/java/de/intevation/flys/client/shared/model/FacetRecord.java,
+	  src/main/java/de/intevation/flys/client/client/services/FeedService.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Minor cosmetics.
+	
+2011-09-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+2011-09-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Refactored to allow specialized controls within the ChartThemePanel, stubby
+	first steps towards such a control (bound to FeedService).
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java:
+	  Minor cosmetics.
+	  (createGrid):
+	  Extracted (trivial) to allow more concrete implementations in subclasses.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java
+	  (createGrid):
+	  Implementation, add a spinner that could talk with feedservice.
+	  (initGrid):
+	  Added new "actions" field.
+	
+2011-09-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Minor Cosmetics.
+
+	* src/main/java/de/intevation/flys/client/server/FeedServiceImpl.java:
+	  Removed superflouus import.
+
+2011-09-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Cosmetics, doc.
+
+	* src/main/java/de/intevation/flys/client/client/services/StepForwardService.java,
+	  src/main/java/de/intevation/flys/client/client/services/StepForwardServiceAsync.java:
+	  Modified docs so that the distinction to FeedService becomes clear.
+
+	* src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java:
+	  Whitespaces.
+
+2011-09-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Added implementation of a FeedService.
+
+	* src/main/java/de/intevation/flys/client/client/services/FeedServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/FeedService.java,
+	  src/main/java/de/intevation/flys/client/server/FeedServiceImpl.java:
+	  Added implementation of a FeedService (shamelessly copied from
+	  ~AdvanceService)
+
+	* src/main/webapp/WEB-INF/web.xml:
+	  Added configuration of feedService.
+
+2011-09-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java:
+	  Set the 'typ' attribute of a feature that is added to the vector layer.
+	  Features, that are loaded into the layer from GeoJSON file keep their
+	  existing 'typ' attribute. New features get the attribute from combo box.
+
+2011-08-31	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Added cross section translation.
+
+	* 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:
+	  Added "cross_section" translation.
+
+2011-08-25	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java:
+	  Moved datacage specific stuff into a VLayout.
+	* src/main/java/de/intevation/flys/client/client/event/DatacageHandler.java:
+	  New. Event handler used by DatacageWidget to broadcast toLoad events.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacagePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageButton.java:
+	  Adjusted calls. TODO: We need the user and the collection view.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Forward collection view to DatacageWindow.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Moved stuff out to DatacageWidget. Send recommendations to CollectionView.
+
+2011-08-25	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Ugly workaraound: If no reference artifact is given use current
+	  artifact as master artifact.
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/ToLoad.java
+	  (toRecommendations): Compile a list of recommendations out of
+	  datacage tree selections. A lot of model conversions. :-/
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactFilter.java,
+	  src/main/java/de/intevation/flys/client/shared/model/FacetFilter.java,
+	  src/main/java/de/intevation/flys/client/shared/model/OutFilter.java:
+	  Added code to convert the models.
+	  
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java:
+	  Removed the UUID of the master artifact from interface.
+	  Its transported in the recommendation. Set the creation
+	  filter if its set in the recommendation.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Recommendation.java:
+	  Added Filter/Facets. Its merely a copy of CreationFilter
+	  but this cannot be serialized from JS to the RPC side.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Adjusted the calls to fulfill the changed interface.
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/Recommendation.java:
+	  Added field for master artifact. Not uses, yet.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Allow passing no master artifact to RPC service when creating new artifact.
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java:
+	  Forward recommendations as a structure to RPC service.
+	  TODO: use this to give filters to creation.
+
+	* src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Adjusted calls.
+
+2011-08-24	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Fix loading of recommendations when collection is not yet in place.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java
+	  (createNewCollection):
+	  Load recommendations when new Collection is created.
+	  (onParameterChange):
+	  Guard call to loadRecommendation to avoid NPE when no collection is yet set.
+
+2011-08-24	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java:
+	Ensure list of recommendations is non-null after construction.
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultRecommendation.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Recommendation.java:
+	  Deleted DefaultRecommendation. Use Recommendation as a class directly.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Use the Recommendation class directly.
+
+2011-08-24	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	Minor cosmetics.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Picky cosmetics about comment content.
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/ToLoad.java,
+	  src/main/java/de/intevation/flys/client/shared/model/FacetFilter.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Use "ids" instead of "num".
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java,
+	  src/main/java/de/intevation/flys/client/server/CollectionHelper.java: 
+	  s/db-ids/ids/g to unify system and user specific loading.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  The toolbar used in this map widget will no longer contain digitize
+	  controls.
+
+2011-08-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactFilter.java,
+	  src/main/java/de/intevation/flys/client/shared/model/ToLoad.java:
+	  Generate unique keys for anonymous artifacts (aka static data).
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DigitizePanel.java:
+	  New. A subclass of the SelectProvider which displays combo boxes on the
+	  left and a map panel in the helper container.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  Added a property that allows initializing the toolbar with no digitize
+	  controls.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java:
+	  Added a method that returns the vector features as GeoJSON string.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Creates a DigitizePanel if the uiprovider string "map_digitize" is set.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapPanel.java:
+	  New. A widget that displays a map with a toolbar. It wraps all the stuff
+	  to combine SmartGWT with GWT-OpenLayers.
+
+2011-08-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/ToLoad.java:
+	  Re-written to feature the new models.
+
+	* src/main/java/de/intevation/flys/client/shared/model/FacetFilter.java:
+	  Be aware of null values for num and name.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Adjusted calls for new toLoad semantics.
+
+2011-08-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactFilter.java,
+	  src/main/java/de/intevation/flys/client/shared/model/FacetFilter.java,
+	  src/main/java/de/intevation/flys/client/shared/model/OutFilter.java:
+	  New. Models which are better suited for creating artifact facet filters.
+	  TODO: Re-factor ToLoad to create these models.
+
+2011-08-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Parse the output modes of the first Artifact only. The output modes of
+	  the other artifacts shouldn't be visible to the user.
+
+2011-08-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/MapInfo.java: New.
+	  Instances of this class are used to store basic information which are
+	  necessary to create maps.
+
+	* src/main/java/de/intevation/flys/client/shared/model/BBox.java: New. An
+	  internal representation of a bounding box which consists of four
+	  coordinates.
+
+	* src/main/java/de/intevation/flys/client/client/services/MapInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MapInfoService.java,
+	  src/main/java/de/intevation/flys/client/server/MapInfoServiceImpl.java:
+	  The interface definitions and the implementation of a service that
+	  queries basic map information from artifact server.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new MapInfo service.
+
+2011-08-22	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,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Re-added the outs parameter to the meta data service. TODO: Pass
+	  it from the UI.
+
+2011-08-22	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/ToLoad.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Aggregate the items to load by artifact id and factories. Makes
+	  it easier to build filter views on new created artifacts to be
+	  added to the current collection.
+
+2011-08-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Set the Collection and its Master-Artifact at first when we create a
+	  CollectionView with an existing Collection instance.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  Enabled the SelectFeature control to select a feature using a box.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  The geometries added by the user are saved as GeoJSON parameters of the
+	  master artifact . The user does not need to save the geometries
+	  explicitly - this is an automatic process. While initializing the map,
+	  those geometries are reloaded and displayed again.
+
+2011-08-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/NoInputPanel.java:
+	  This panel is used to suppress the creation of an input panel.
+	  Currently, this panel is used in the floodmap state. This state is able
+	  to save geometries, but we currently don't want to insert GeoJSON
+	  strings manually.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Instantiate the NoInputPanel if the ui provider string "noinput" is
+	  specified.
+
+2011-08-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Determine the max extent of all layers contained in the collection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java: Set
+	  the map projection and its max extent determined by MapOutputTab.
+
+2011-08-12	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	Extract selected data from datacage panel
+
+	* src/main/java/de/intevation/flys/client/server/meta/Converter.java:
+	  Removed some debugging.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ToLoad.java: New.
+	  Contains artifact id, db ids, factory name and facet number. Should
+	  be enough to identify the data to load.
+	* src/main/java/de/intevation/flys/client/shared/model/AttrList.java:
+	  Fixed indexing bug.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Added a toLoad() method which returns a list of ToLoad data to load via
+	  the corresponding factories.
+	  If you double click on a tree node all data below this filled into this list
+	  and the window is disposed.
+	  You can do multipl selects on the and press the '+' button to do
+	  a multiselect loading.
+
+2011-08-09	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/server/meta/Converter.java:
+	  Do not use 'name' attribute of datacage.
+
+	* src/main/java/de/intevation/flys/client/client/widgets/tree/AttributedTreeNode.java:
+	  Removed. Not longer needed.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Build the UI tree in a more smartgwt compatible style. Prevents lots
+	  of exception when using click listeners.
+
+2011-08-09	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Set window title to include UUID.
+
+2011-08-09	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Changed log message for two distinct malconditions.
+
+2011-08-09	Felix Wolfsteller	<felix.wolfsteller@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Adjusted German translation to use unique strings.
+
+2011-08-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/BooleanPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java,
+	  src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Removed superfluous imports.
+
+2011-08-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/widgets/tree/AttributedTreeNode.java:
+	  New. Subclassed TreeNode to carry the attribute data from
+	  the meta data service.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Create AttributedTreeNodes instead of TreeNodes to have the
+	  relevant information at hand if a listener wants to access
+	  the data.
+
+2011-08-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/shared/model/AttrList.java:
+	  New. List of attributes for each DataCageNode.
+
+	* src/main/java/de/intevation/flys/client/server/meta/Converter.java:
+	  Generate the list of attributes for each datacage node.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataCageNode.java:
+	  Added the attribute list.
+
+2011-08-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Do not cry for missing i18n if running into error.
+
+	* src/main/java/de/intevation/flys/client/server/meta/Converter.java:
+	  Convert more stuff.
+	  
+	* src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java:
+	  Create correct request document now.
+
+2011-08-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de> 
+
+	* src/main/java/de/intevation/flys/client/client/services/MetaDataService.java,
+	  src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/MetaDataServiceAsync.java:
+	  Take the user id, too.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Adjusted call to pass the user id to the datacage window.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageButton.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacagePanel.java:
+	  TODO: Bring user id into play.
+
+2011-08-02  Felix Wolfsteller <felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  Unified spacing and punctuations in javadoc comments, removed
+	  incorrect comment.
+
+2011-07-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:
+	  Collections are able to store the recommendations they have already
+	  loaded. Therefore, new methods have been added to add recommendations
+	  and to determine, if a recommendation has already been loaded.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Read the already loaded recommendations from Collection's DESCRIBE
+	  document.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Write the Collection's recommendations into its attribute document.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Two things:
+	   1) Do no load recommendations that have already been loaded before.
+	   2) Update UI when there are no more artifacts to load (recommendations).
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Added missing slash in XPath expression.
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultRecommendation.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Recommendation.java:
+	  New. The interface and its default implementation that represent
+	  recommendations provided by an Artifact. A recommendation consists at
+	  least of the name of an ArtifactFactory and a list of database ids.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Read the recommendations from Artifact's DESCRIBE document (which
+	  structure has changed.)
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java:
+	  Recommendation are no longer simple strings but Recommendation objects
+	  now. So, the interface to retrieve those recommendations has changed.
+
+	* src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java:
+	  This service takes a further string now, which represents the database
+	  ids used to create static artifacts that require data from database.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Adapted the call of the ArtifactService.
+
+M      src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java: The
+	  vector layer which is used to save features created by the user acts as
+	  OpenLayers base layer. I decided to do so, because this is the first
+	  and only layer that will always exist in our maps. Users aren't able to
+	  remove it!
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java:
+	  Added a callback interface that allows the MapOutputTab to react on
+	  modifications in the ThemePanel. This special callback is used to
+	  activate/deactivate layers.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Now, the layers in the map reflect the facets of the map output. Each
+	  layer's extent is analyzed and finally the map is zoomed to the minimal
+	  extent that contains all layers.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ThemeNavigationPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ThemeNavigationPanel.java:
+	  Moved the panel a package level higher.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java: New.
+	  Abstract class that consists basically of the code that was removed from
+	  ChartThemePanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Sourced code base out to ThemePanel, so that's possible to reuse the
+	  code for a MapThemePanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java:
+	  New. A theme panel for the map widget.
+
+2011-07-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/AttributedTheme.java:
+	  New. This theme stores all its attributes in a map. The intent of this
+	  theme is to save all the facet's values contained in the DESCRIBE of the
+	  Collection to be able to write an equivalent attribute part as the
+	  incoming DESCRIBE has -> no loss of attributes like WMS URL or
+	  layernames in a map theme!
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Instantiate new AttributedThemes to save all information of a facet.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Write all attributes of an AttributedTheme into a facet's element.
+
+	* 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/FacetRecord.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  The 'active' member variable is an integer value now as it is in the
+	  Collection's DESCRIBE document, too.
+
+2011-07-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java:
+	  An artifact can be created using a UUID as model artifact.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Read artifact recommendations from artifact description.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java:
+	  Added method to retrieve recommendations.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Adapted the call of the ArtifactService.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  If the Artifact of a collection changes, the recommended Artifacts - if
+	  existing - are created and added to the current Collection.
+
+2011-07-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MeasureControl.java:
+	  New. This control consists of two OpenLayers controls to measure lines
+	  and polygons.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  Added the MeasureControl.
+
+	* src/main/webapp/images/measurePolygon.png,
+	  src/main/webapp/images/measurePolygon_Selected.png,
+	  src/main/webapp/images/measureLine.png,
+	  src/main/webapp/images/measureLine_Selected.png: New. Icons for the
+	  measure control.
+
+	* 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: Added
+	  path locations for the measurement tools.
+
+2011-07-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapPositionPanel.java:
+	  New. This panel displays the mouse position in map coordinates.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/DrawControl.java::
+	  New. This control consists of a button to enable the control and a
+	  combobox that provides different types of barriers. Dependent on the
+	  selected barrier type, the user can draw lines or polygons into the map.
+
+	* src/main/java/de/intevation/flys/client/client/utils/EnableDisableCmd.java:
+	  New. This interface might be used to react on enable/disable operations.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  Added controls to digitize lines and polygons.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java:
+	  Added a method that returns a vector layer that might be used to insert
+	  new barriers draw by the user. This layer is used by the DrawControl.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  The MapToolbar requires a Canvas object now, which is used to keep track
+	  on the current mouse position.
+
+	* src/main/webapp/images/selectFeature_Selected.png,
+	  src/main/webapp/images/selectFeature.png,
+	  src/main/webapp/images/digitize.png,
+	  src/main/webapp/images/digitize_Selected.png,
+	  src/main/webapp/images/removeFeature.png: New. Icons for the map control
+	  elements.
+
+	* 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
+	  i18n strings and image pathes.
+
+2011-07-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapToolbar.java:
+	  New. This toolbar provides controls that interact with the map.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java:
+	  Removed the initialization of map controls.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Added the MapToolbar at the top of the layout.
+
+2011-07-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Finished layouting the components. The map has the same layout as the
+	  chart now.
+
+2011-07-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: Added a dependency to GwtOpenLayers 0.5 (Apache 2.0).
+
+	* src/main/java/de/intevation/flys/client/FLYS.gwt.xml: Include the GWT
+	  OpenLayers code.
+
+	* src/main/webapp/FLYS.html: Include OpenLayers 2.9 (currently from remote
+	  openlayers.org server). For production use, we have to include a local
+	  OpenLayers.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Small
+	  modification on the way to render the HTML page. This lets us now
+	  combine GWT and SmartGWT components.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/FloodMap.java:
+	  New. This components manages the map provided by GwtOpenLayers.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  Initializes a FloodMap now and renders the map in the middle area. Note,
+	  that the layout is nearly completely using GWT components. Mixing
+	  GwtOpenLayers with SmartGWT doesn't work here.
+
+2011-07-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/OutputMode.java: An
+	  output mode has type member now. In addition, the interface describes a
+	  method that creates an OutputTab. Concrete subclasses should return
+	  OutputTabs that fit to their type.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ChartMode.java:
+	  New. This OutputMode is instantiated if type == "chart" and creates
+	  ChartOutputTabs.
+
+	* src/main/java/de/intevation/flys/client/shared/model/MapMode.java: New.
+	  This OutputMode is instantiated if type == "map". It creates
+	  MapOutputTabs.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultOutputMode.java:
+	  This default implementation of an OutputMode support the type member
+	  now. The method to create OutputTabs will return null.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Now, we support the 'type' attribute of Outputs in the DESCRIBE
+	  document. Based on the type, we will now instantiate concrete subclasses
+	  of the DefaultOutputMode. E.g. the type == 'chart', the instantiated
+	  OutputMode will be ChartMode.
+
+	* src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java:
+	  New. This OutputTab is used to render a map.
+
+	  Note: This is currently a stub only!
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Creating OutputTabs based on the names of an OutputMode is obsolete.
+	  Now, the OutputModes itself create their required OutputTab.
+
+2011-07-07  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 strings for the output mode 'floodmap'.
+
+2011-07-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/BooleanPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TextProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AutoIntegerPanel.java:
+	  Default values provided by the data list used to create the panels are
+	  set now.
+
+2011-07-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Refactored getting labels out to own methods which enables subclasses to
+	  changed them.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WaterlevelGroundPanel.java:
+	  Override methods of DistancePanel to support own labels for this panel.
+	  In addition, this panel has no distance/locations table as the
+	  DistancePanel has.
+
+	* 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 strings for the distance panel and the waterlevel/ground panel.
+
+2011-07-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/BooleanPanel.java:
+	  This panel is a sublcass of TextProvider now. The difference to the
+	  TextProvider is, that there is the option to active/deactivate a
+	  checkbox. So, the valid values returned by this panel are "true" and
+	  "false".
+
+2011-07-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/TextProvider.java:
+	  Made methods in this class much smaller to override them easily in
+	  subclasses. In addition, the TextItem has been replaced with a
+	  DataSourceField which seems to be more easily to change in subclasses.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AutoIntegerPanel.java:
+	  Override required methods of TextProvider. This panel now accepts
+	  integer values and the string "auto". An inner class
+	  AutoIntegerValidator is used to validate the user input.
+
+	* src/main/webapp/FLYS.css: Set style for html label elements. Those label
+	  elements are used in forms to label items.
+
+2011-07-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/ParameterChangeEvent.java:
+	  This event now supports a further member variable "type". Type is an
+	  enum and can be FORWARD or BACK which reflects to stepping forward or
+	  backward with the master artifact.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Improved the way of adding/removing items to/from the static list of
+	  parameters. We now distinguish between the two ways:
+	  - step forward: the last item in the list is added to the static
+	                  parameter list. This makes redrawing much smoother.
+	  - step backward: only the necessary items are removed from the static
+	                   parameter list. Before, we have removed all items and
+	                   added the relevant ones afterwards. This makes
+	                   redrawing also much smoother.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ContinuePanel.java:
+	  This panel will have no static widget.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AutoIntegerPanel.java:
+	  This panel uses the parents createOld() now.
+
+2011-07-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/webapp/FLYS.css: Some layout improvements related to the height
+	  of components.
+
+2011-07-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  The right side of the ParameterList now consists of a stacked panel that
+	  itself consists of the helper panel and a panel for displaying the
+	  calculation results. The user is now able to see both at the same time.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Improved the Collection update process. There is no need to update the
+	  collection twice when the master artifact changed. Now, the collection
+	  is updated only a single time if the collection itself changed - and it
+	  changes everytime when its artifact is set.
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  The label 'Berechnungsergebnisse' is no longer part of the Canvas
+	  created by this class.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  The helper panel is now a VLayout, no longer a Canvas.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DistancePanel.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/SingleLocationPanel.java:
+	  Add helper widgets as members to the helper panel, no longer as
+	  children.
+
+	* 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 input support stack.
+
+2011-07-05  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/DistancePanel.java:
+	  Modified these classes with the intent to override them easier.
+
+	* src/main/java/de/intevation/flys/client/client/ui/BooleanPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacagePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/TextProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AutoIntegerPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WaterlevelGroundPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageButton.java:
+	  New panels for data input. The work on these classes is not finished
+	  yet!
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Support more UI providers that return the new panels above.
+
+	* 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: New
+	  strings used in the WaterlevelGroundPanel.
+
+2011-07-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/AdvanceHandler.java:
+	  New. This interface describes a handler that listens to events in order
+	  to advance to a next step.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ContinuePanel.java:
+	  New. This panel doesn't allow users to enter data. It only displays a
+	  link that lets users step forward to a next state.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  The ParameterList needs to take care on states that do not require user
+	  input but that aren't the final state of an artifact. In such case, the
+	  UI provider should be the ContinuePanel only! It allows users to step to
+	  the next state. In order to provide such functionality - step forward
+	  without feeding the artifact - the ParameterList now implements the
+	  AdvanceHandler.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  If the uiprovider attribute is 'continue', the ContinuePanel is
+	  returned.
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Adapted the width of the label field.
+
+	* 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
+	  strings for new states.
+
+2011-07-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Added the missing "back"-button.
+
+2011-07-01  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue159 (WINFO: Radiobutton - Ortsauswahl bei "W für ungleichwertigen Abflusslängsschnitt" entfernen)
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Connected columns of the locations and distances tables with the
+	  panels from/to fields. The first two columns of the locations and the
+	  first column of the distances table might be used to set from/to values
+	  in the panel.
+
+2011-07-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DistanceInfoXML.java:
+	  New. This service - which is no GWT RPC Service - returns the distance
+	  info document as XML. The content of the document is not modified,
+	  parsed but only copied through.
+
+	* src/main/java/de/intevation/flys/client/client/ui/range/DistanceInfoDataSource.java:
+	  New. This data source is used to fill the LocationsTable and RangeTable.
+	  It uses the DistanceInfoXML service to query the required data
+	  automatically using a "locations" or "distances" filter to reduce the
+	  size of the data document.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Use the DistanceInfoDataSource to fill the locations and distances tables.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new service.
+
+2011-06-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/range/LocationsTable.java,
+	  src/main/java/de/intevation/flys/client/client/ui/range/RangeTable.java:
+	  New. These two tables are used to support the user to select from/to
+	  values in the distance panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  Added LocationsTable and RangeTable that currently load no data.
+
+2011-06-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DistancePanel.java:
+	  New. This panel is used to insert kilometer ranges. There is no input
+	  mode for locations as it is in LocationDistancePanel.
+
+	* 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 i18n strings for the distance panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  Added a method to set the step width.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Create a DistancePanel UIProvider if the "uiprovider" string in the
+	  DESCRIBE of an artifact equals "distance_panel".
+
+2011-06-29  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue185 (IE8 + Chromium: Kein Zoomen von Diagrammen möglich)
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Create the chart image first before initialising listeners on it.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ZoomboxControl.java:
+	  The zoombox that is displayed in while zooming is 2 pixel smaller than
+	  the area between start and end point now. This should avoid the zoombox
+	  absorbing mouse events.
+
+2011-06-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Reordered delete and rename in project context menu, add separator.
+
+2011-06-29  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue181 (Erstes Thema in der Themenliste wählt sich automatisch wieder an)
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  We will no longer check if an update of the theme list is necessary.
+	  The EditEvent that is caught should be enough information to start the
+	  update process.
+
+2011-06-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: GWT version upgrade from 2.1.1 to 2.3.0.
+
+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:51 2012 +0200
@@ -0,0 +1,266 @@
+2011-09-19  RELEASE 2.5
+
+    NEW:
+
+        * Added new widgets:
+          - DistancePanel: allows users to enter a km range.
+          - ContinuePanel: offers a 'next' button only for states which do not require input data.
+          - BooleanPanel: allows users to answer for yes/no questions.
+          - TextProvider: allows users to enter text strings.
+          - AutoIntegerPanel: allows users to enter integer values or the value 'auto'.
+          - WaterlevelGroundPanel: allows users to enter three integer values for "from", "to", and "diff" used for floodmaps.
+          - DatacagePanel: displays a datacage widget, that allows users to select items from datacage.
+          - MapPanel: displays a Gwt-OpenLayers based map and a MapToolbar.
+          - DatacageWidget: reads XML documents from MetaDataService and displays the result as tree.
+          - DigitizePanel: allows users to digitize spatial geometries using a map based on Gwt-OpenLayers.
+          - DatacageTwinPanel: allows users to select items from two parallel datacage widgets.
+
+        * Added RPC services to:
+          - create new Artifacts based on a model Artifact (ArtifactService).
+          - read XML documents from MetaDataService (MetaDataService).
+          - read map information (as srid, extent) from MapInfoService (MapInfoService).
+          - feed an Artifact only (without forwarding into a new state) (FeedService).
+          - fetch the description of an Artifact only (DescribeArtifactService).
+          - create new Artifacts based on Recommendation objects and add them to
+            current Collection (LoadArtifactService).
+
+        * The DatacageWindow in charts now allows adding new themes to charts.
+
+        * Introduced a Recommendation mechanism in the CollectionView that loads Artifacts to the
+          current Collection based on the recommendations in the Artifact description of the master
+          Artifact.
+
+        * Extract background state/messages of master Artifacts. Lock CollectionView window if
+          master Artifacts have started background threads that have not finished yet and display
+          status messages in the lock screen.
+
+        * Added support to clone existing projects in the project list.
+
+        * Introduced copy&paste support in calculation result tables.
+
+        * Added new OutputTab MapOutputTab. This tab initializes map based on Gwt-OpenLayers.
+          The layers added to the map depend on the facets of the output.
+
+        * Added a CrossSectionChartThemePanel. This ThemePanel offers a spinner right besides the
+          theme name to modify the current km.
+
+        * Added a MapToolbar that offers controls for working with a map based on Gwt-OpenLayers.
+
+        * UI improvements:
+          - Stepping back/forward in parameterization no longer redraws the whole CollectionView.
+          - set the title of CollectionView windows when the name of a collection is modified in the project list.
+          - introduced "find-as-you-type" search in most input tables.
+
+        * Distinguish between 'chart' and 'map' output modes to create ChartOutputTabs or MapOutputTabs.
+
+        * Bumped GWT up to 2.3.0.
+
+
+    FIXES:
+
+        * flys/issue159 (WINFO: Radiobutton - Ortsauswahl bei "W für ungleichwertigen Abflusslängsschnitt" entfernen)
+
+        * flys/issue166 (Projektname in die Fensterleiste übernehmen)
+
+        * flys/issue181 (Erstes Thema in der Themenliste wählt sich automatisch wieder an)
+
+        * flys/issue185 (IE8 + Chromium: Kein Zoomen von Diagrammen möglich)
+
+        * flys/issue194 (Wasserspiegellagen: Hauptwerte Q/D Spaltenbezeichnung "Kenzeichnender Abfluss")
+
+        * flys/issue195 (Wasserspiegellagen: Q/D-Panel Spaltenbezeichnung "W/Q/D" -> "Q/D")
+
+        * flys/issue199 (Berechnete Abflusskurve / Dauerzahlen: Freies Textfeld akzeptiert mehr als einen Ort.)
+
+        * flys/issue203 (W-INFO: Wasserspiegellagenberechnung / Auswahlunterstützung Abfluss)
+
+        * flys/issue234 (W-INFO: Projektverwaltung)
+
+        * flys/issue239 (W-INFO: Wasserspiegellagenberechnung / Auswahlunterstützung Ort bzw. Strecke)
+
+        * flys/issue240 (W-INFO: Projektverwaltung / Einstellung dauerhaft)
+
+        * flys/issue259 (Daten aus Datenkorb in Diagramm einladen)
+
+        * flys/issue266 (Projektverwaltung: Duplizieren von Projekten)
+
+        * flys/issue282 (Karte: Abstand interpolierte Profile - Default wert)
+
+        * flys/issue285 (Projektverwaltung: Suche auf den Titeln)
+
+        * flys/issue286 (Karte: Tooltips für die Werkzeugleiste)
+
+        * flys/issue289 (Karte: Messen von Strecken und Flächen in falscher Dimension)
+
+        * flys/issue299 (Karte: Einige Farben vereinheitlichen)
+
+        * flys/issue293 (Karte: Farbliche Anpassung der digitalisierten Objekte)
+
+        * flys/issue305 (Suche: Löschen der Eingabe nach einem Wechseln des Tabs Strecke -> Ort)
+
+        * flys/issue309 (Querprofil: Manuelle Eingabe funktioniert nicht nach Return (nur nach Tab))
+
+        * flys/issue311 (Querprofil: i18n)
+
+        * flys/issue313 ("AssertionError" beim Wechsel von Deutsch zu Englisch in der Projektliste)
+
+
+
+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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,552 @@
+NEWS
+
+2011-09-19  Release V 2.5.0
+
+== Version 2.5.0 (19.09.2011, rev. 2786) ==
+
+Diese Seite stellt primär Neuerungen in dieser Version aus Sicht des Nutzers zusammen bzw. verweist auf Vorarbeiten, die für jeweilige Funktionen benötigt werden.
+
+=== Zusammenfassung und Hintergedanken zur Version ===
+Die Version 2.5 verfolgt die folgenden Ziele auf den unterschiedlichen Ebenen:
+ * Bestehende textbasierte Daten von FLYS für die Gewässer Saar, Mosel und Elbe über Importmechanismen vollständig in die Datenbankhaltung samt dazugehörigen Datenmodell übernehmen
+ * Strukturierung von Geodaten und Import dieser in die Datenhaltung
+ * den Datenkorb als zentralen Datenmanager für statische und dynamische Daten bereitzustellen und in den unterschiedlichen Anwendungskontexten -- inline: Parameterisierung Überschwemmungsfläche, Parameterisierung Differenzen und als externes Fenster: Zuladen manueller Daten -- zugreifbar zu machen
+ * die Karte als Auswahlunterstützung und zur Anzeige von gerechneten Überschwemmungsflächen bereitzustellen
+ * die Integrationsfähigkeit der Karte in die Projektverwaltung zu ermöglichen
+ * Integration und Ansteuerung von WSPLGEN als asynchronen Prozeß in einer Mehrbenutzerumgebung zu integrieren
+ * Infrastruktur zur Ansteuerung und Generierung von Karten bereitzustellen
+ * die Projektverwaltung abzuschließen, um die Gesamtarbeitsweise im System greifbar zu machen.
+ * die Anwendung auf seine Zieldatendank Oracle 11 zu bringen
+ * das Gesamtsystem in seiner Zielumgebung SLES 11 bereitzustellen
+ * Erste Erfahrungsmöglichkeit mit FLYS togo bereitzustellen
+ 
+=== Framework ===
+
+ * Allgemeine Steuerung
+
+ * Projektverwaltung
+  * Projekte umbenennen
+  * Projekte dublizieren
+  * Titel der Projekte nach Umbenennung in jeweiligem Projektfenster anzeigen
+  * Filterung der Projekteliste auf Basis der Titel
+
+
+=== Fachdatenbestand ===
+
+ * Datenmodell
+  * Erweiterung um hyk-Datenbestand
+  * Erweiterung im Querprofildatenbestand
+  * Erweiterung im räumliche Tabellen für Kartenthemen 
+
+ * Importer hydrologischer und geodätischer Daten
+  * Querprofiledateien (*.prf)
+  * hyk-Dateien
+
+ * Importer von Geodaten
+  * semiautomatischer Mechanismus zum Import von Shapedateien in Oracle 11
+
+
+
+=== WINFO ===
+
+ * Gewässer- und Pegelinfos
+  * Einführung einer sog. Ziehharmonika, um Pegelinfos fortwährend integrieren zu können.
+ * Wahl von Strecke und Ort
+  * Filterung auf Streckenfavoriten und Orten durch Eingabe eines Begriffes - [[https://roundup-intern.intevation.de/flys/issue141|Details]]
+ * Wahl von Abflüssen, Wasserständen und Dauerzahlen
+  * Bei Wasserspiegellagenberechnungen können Qs nur noch angezeigt werden, wenn eine Berechnung auf freier Strecke gestartet wird.
+
+ * Berechnungsausgabe
+  * Wasserspiegellagen
+   * Querprofile
+    * Darstellung der Querprofildatensätze aus der Datenbank
+    * Manuelle Eingabe von Kilometern
+     * Zur Zeit sind noch folgende Aspekte limitiert:
+      * Anzeige einer Wasserspiegellage und eines Querprofildatensatzes
+      * Durchlaufen des Kilometerzählers in 5 km Schritten
+      * kein synchrones bzw. asynchrones Navigieren möglich
+   
+   * Längsschnitt
+    * Anzeige von Streckenfavoriten
+    * Manuelles Hinzuladen früherer Berechnungen
+
+  * Abflusskurven
+   * Automatisches Anzeigen von Hauptwerten aufgrund einer Vorbelegung
+   * Datenkorb: Exemplarische Darstellung des Gesamtbestandes von früheren und datenbankbasierten Inhalten
+
+  * Überschwemmungsfläche (aufgrund zu wenig strukturierter Geodaten kann dies erstmalig an der Saar getestet werden!)
+   * Parameterisierung:
+    * Zugriff auf beliebige Wasserspiegellagen
+    * Auswahl eines beliebigen digitalen Geländemodells am Gewässer
+    * Digitalisierung von Rohren, Gräben und Dämmen
+    * Löschen bestehender Rohren, Gräben und Dämmen
+    * TODO: Berücksichtigen einer eigenen Shapedatei bei Berechnungstyp "Szenario"
+   * Durchführung der Berechnung:
+    * Informieren des Nutzers zum Fortgang der Berechnung
+     * TODO: Abbrechen der Berechnung
+    * Während der Berechnung kann das Projekt geschlossen werden und zu einem beliebigen Zeitpunkt wieder geöffnet werden. Der jeweilige Status wird dem Nutzer angezeigt.
+   * Technik/Hintergrund
+    * Ansteuerung von WSPLGEN mit seiner Parameterisierung und Bereitstellen der dazugehörigen Daten
+    * Management mehrerer gleichzeitiger WSPLGEN-Berechnungen
+    * Automatische Generierung einer WMS-Ebene zur Visualisierung des berechneten Überschwemmungsgebietes auf Basis einer Shapedatei unter Berücksichtigung der Eingabe von Differenzen zwischen Wasserspiegellage und Gelände
+   * Anzeige der Überschwemmungsfläche in der Karte
+    * siehe Abschnitt "Karte"
+
+  * Differenzen
+   * Auswahl beliebiger Datenreihen aus dem Datenkorb
+    * TODO: Erweiterung der Konfiguration zur Filterung kompatibler Datensätze
+    * TODO: Erweiterung zu einer Mehrfachauswahl von Datenreihen
+    * TODO: Löschen von ausgewählten Differenzen aus der Tabelle
+   * Darstellung in einem Längsschnitt-Diagramm
+    * Hervorhebung der Null-Linie
+    * TODO: Anzeige der dazugehörigen Datenreihen mit absoluten Werten
+   * Export der Differenzen in einer CSV-Datei
+
+
+=== Karte ===
+    * Öffnen der Karte mit generiertem Überschwemmungsgebiet
+    * Themensteuerung Rohren, Gräben und Dämmenuerung (analog zum Diagramm)
+     * TODO: Anzeige einer Legende
+    * Kartennavigation
+     * Gesamtübersicht
+     * Hineinzoomen/Herauszoomen
+     * TODO: Layer-Zoom
+     * Karte verschieben
+     * Messen von Strecken
+     * Messen von Flächen
+     * Anzeige der Cursorkoordinaten im aktuellen Referenzsystem
+      * TODO: Auswahl alternativer Referenzsysteme
+     * TODO: Informationswerkzeug
+     * TODO: Integration des Datenkorbs
+     * TODO: Export der Karte als JPEG, PNG, GeoTiff
+
+
+=== Diagramme ===
+
+  * Interna:
+
+  * Themensteuerung
+   * Automatische Vorbelegung von Themen (technisch sog. recommendations)
+    * Beispiele
+     * Längsschnitte: Automatisches Einladen von Streckenfavoriten
+     * Abflusskurve: Automatisches Vorbelegung von Haupt- und Extremwerten
+
+ * Werkzeugleiste
+
+
+=== Datenkorb ===
+ * Interna:
+  * "Mitlesen" früherer Berechnungen und Speicherung in einer speziellen Datenhaltung zur Bereitstellung in der Nutzeroberfläche
+  * Integration datenbankbasierter Inhalte (statisch) und nutzerspezifischer Berechnungen (dynamisch) zur Anzeige in einer Struktur
+  * Erstellen eines übergreifenden Konfigurationsmechanismus zur Umsetzung diagrammspezifischer Filterungen
+   * TODO: Erweiterung der Filterung auf bestehenden Diagrammtypen; bisher noch beispielhaft
+  * Mechanismus zum Einfügen statischer und dynamischer Daten aus der GUI in die Anwendung, die entsprechende Datensätze aus beiden Quellen heranzieht und automatisch in die Diagrammgenerierung übernimmt.
+  * Bereitstellen sogenannter "Recommendations" zur Umsetzung von Vorbelegungen
+  
+ * Manuelles Nachladen von Themen in Diagramme
+  * Beispiel: Längsschnitt: Einladen Früherer Berechnungen
+
+=== Themenstil-Editor ===
+ * Intern: 
+  * Weitere Diagrammtypen mit Vorbereitungen zur Implementierung der GUI versehen
+
+
+=== Technische Umgebung ===
+ * Integration der Testsysteme in SUSE Linux Enterprise Server 11, SP1 durchgängig auf Softwarepaketen, die von SUSE gepflegt werden (verringerte Wartung), ausgenommen
+ * WMS-Kartendienst: UMN MapServer 6.0.1 mit Anbindung an Oracle 11, bereitgestellt als RPM-Paket (Installer)
+ * Geodaten-Importer: GDAL 1.8.0 mit Anbindung an Oracle 11, bereitgestellt als RPM-Paket (Installer)
+ * Bestehendes WSPLGEN (unter Linux lauffähig gemacht)
+
+=== FLYS togo ===
+
+ * Umgebung
+  * Integration des Systems in SUSE Linux Enterprise Server 11, SP 1
+
+ * Software
+  * Installation von UMN MapServer 6.0.1
+  * Installation von GDAL 1.8.0
+  * Installation von WSPLGEN
+  * Installation des Geodatenimporters auf Basis von ogr2ogr
+  * Installation des Importers für hydrologische und geodätische Daten
+  * Installation von FLYS 2.5
+  * Installation von QuantumGIS als Testclient für WMS und Shapedateien (nur zu internen Testzwecken; jenseits des Projektes)
+
+ * Datenbestand und Import
+  * Import der hydrologischen, geodätischen für Saar, Mosel und Elbe (außer prfs) in Oracle 11
+  * Import der Geodaten der Saar in Oracle 11 auf Basis des semi-automatischen Mechanismuses
+  * Konfiguration datenbankspezifischer Geodaten über einen WMS-Dienst aus Oracle 11
+
+ * Anwendung
+  * Ablauffähigkeit der Anwendung (ausgenommen Karte) auf Oracle 11
+
+
+=== Offene Punkte ===
+ * Zur vereinfachten Übersicht sind die TODOs in die jeweilige Unterkapitel dieser Seite integriert.
+
+
+=== Liste aller bearbeiteten Issues ===
+
+{{{#!CSV ,
+"ID","Titel"
+"id","title"
+268,"WSPLGEN findet Feldname STATION und WSP nicht"
+264,"Geodaten: Import - technisch-fachliche Evaluierung"
+266,"Projektverwaltung: Duplizieren von Projekten"
+256,"Datenkorb: XXX Issue festhalten"
+280,"BoundingBoxen von Streckenfavoriten und Haupt- und Extremwerten unsichtbar machen"
+258,"Datenkorb: Outs statt States führen"
+254,"Datenkorb: Klonen von Artefakten in anderen Collections + Facettenfilter zum Ausblenden"
+313,"""AssertionError"" beim Wechsel von Deutsch zu Englisch in der Projektliste"
+286,"Karte: Tooltips für die Werkzeugleiste"
+181,"Erstes Thema in der Themenliste wählt sich automatisch wieder an"
+234,"W-INFO: Projektverwaltung"
+262,"Datenkorb: Vereinigung der beiden Konfigurations-Templates"
+259,"Daten aus Datenkorb in Diagramm einladen"
+281,"Karte: Auswahl der berechnten Wasserspiegellage über Inline-Datenkorb"
+314,"Datenkorb: Neue Berechnungen in laufender Sitzung werden nicht im Datenkorb angezeigt"
+283,"Anzeige des Datenkorbs auf früheren Stand bringen"
+159,"WINFO: Radiobutton - Ortsauswahl bei ""W für ungleichwertigen Abflusslängsschnitt"" entfernen"
+260,"Datenkorb: Masterartefakt in View aufführen"
+279,"WINFO: Elbe Wasserspiegellage - Index Out of Bounds"
+20,"Versions-Clash bei slf4j verhindert Start des Artefakt-Servers."
+290,"Karte: Eingabe von Differenzen zw. WSP und Gelände findet keine Ausprägung in der Karte"
+310,"Querprofil: Farben der Themen"
+309,"Querprofil: Manuelle Eingabe funktioniert nicht nach Return (nur nach Tab)"
+311,"Querprofil: i18n"
+291,"Karte: Farbdarstellung und Kartenkonfiguration von Desktop-FLYSübernehmen"
+135,"Diagramm: Trotz abgeschalteter Themen bleiben Beschriftungen bestehen"
+191,"AT-Export: Längsten monoton steigenden Bereich exportieren."
+219,"W-INFO: Abflusskurvenberechnung / keine Themen im Diagramm"
+176,"Diagramm: Benennung eines Abflusses bei gewählter Höhe am Pegel"
+160,"WINFO: Auswahltabelle Orte bei Modus Strecke nach Step-Back"
+180,"WINFO: Zeilen der Tabelle können nach der Markierung in die Zwischenablage kopiert werden."
+282,"Karte: Abstand interpolierte Profile - Default wert"
+}}}
+
+
+
+== Version 2.4.0 (27.06.2011, rev. 2257) ==
+
+Diese Seite stellt primär Neuerungen in dieser Version aus Sicht des Nutzers zusammen bzw. verweist auf Vorarbeiten, die für jeweilige Funktionen benötigt werden.
+
+=== Zusammenfassung und Hintergedanken zur Version ===
+Die Version 2.4 verfolgt für WINFO das Ziel die fachliche Nachprüfbarkeit der bestehenden Berechnungsarten bereitzustellen. Hierbei sind folgende Aspekte wichtig:
+ * fachliche Richtigkeit und Genauigkeit der bestehenden Berechnungsarten
+ * Bereitstellung und Unterstützung eines Berichtswesens in den Berechnungsvorgängen
+ * Vorbereitung der Berechnungsarten für den Mehrbenutzerbetrieb
+ * Vollständigkeit der benötigten Fachdaten aus dem FLYS-Datei-Server für bestehende Funktionen (Kantenbilder, Typisierung Streckenfavoriten)
+ * Export aller benötigten Berechnungsdaten in entsprechenden Formaten (WST, WST korrigiert, CSV, AT)
+
+Um diese Ergebnisse visualisieren und im konsolidierten Diagrammbereich testen zu können, stellt das Diagramm die zentralen interaktiven Funktionen zur Navigation und Themensteuerung bereit und prüft die Kopplung von Client und Server Zusammenspiel. Um dem Nutzer die Verbindung seines Gesamtworkflows zu ermöglichen, ist die Projektverwaltung mit interaktiven Funktionen ausgestattet worden. Diese setzt unterschiedliche Parameterisierungen und Darstellungen in Projekten in Zusammenhang.
+
+=== Framework ===
+
+ * Allgemeine Steuerung
+  * Ehemalige Schriftzüge als Schaltflächen darstellen
+
+ * Projektverwaltung
+  * Projektliste automatisch aktualisieren
+  * Projekt per Klick dauerhaft speichern
+  * Projekte mit eigenem Titel versehen
+  * Projekte manuell löschen
+
+
+=== Fachdatenbestand ===
+ * Importer
+  * Oberkanten und Unterkanten aus KM-Dateien extrahieren.
+  * Typisierung einzelner Objekte als Pegel, Brücke, Hafen, Zufluß, etc.
+
+ * Analyse Datenmodellierung bestehender Geodaten in Shape-Dateien
+  * Bereitstellung eines WMS-Dienstes für die Saar
+
+
+=== WINFO ===
+ * Wahl von Strecke und Ort
+  * Anzeige von Oberkante und Unterkante zu einem jeweiligen Ort
+  * Suche in der Tabelle - Details https://roundup-intern.intevation.de/flys/issue141
+
+ * Wahl von Abflüssen, Wasserständen und Dauerzahlen
+  * Verteilen von Q/D und W auf zwei Reiter
+
+ * Berechnungsausgabe
+  * Integration von Berechnungsberichten als Meldungen/Warnungen
+   * TODO: Beschreibungen verbessern und übersetzen.
+  * Wasserspiegellagen
+   * Interne Berechnung korrigiert und Genauigkeit geprüft. - https://roundup-intern.intevation.de/flys/issue96
+  * Abflusskurven
+   * Ausgabe der berechneten Werte
+   * bzgl. Export siehe dort
+  * W bei ungleichwertigem Abflusslängsschnitt
+   * Interne Berechnung korrigiert und Genauigkeit geprüft.
+
+
+=== Diagramme ===
+
+  * Interna:
+   * Infrastruktur geschaffen, um jeweilig Berechnungsergebnisse als einzelnes Thema auswählbar zu machen (technisch: Fazetten)
+
+  * Themensteuerung
+   * Bereitstellung einer Themenliste
+   * Aktivieren und Deaktivieren der Themenliste mit automatischem Nachlademechanismus
+   * Verschieben von Themen schrittweise
+   * Verschieben von Themen nach ganz oben oder unten
+   * TODO: Alle markierten Themen an-/abwählen
+   * TODO: Alle markierten Themen löschen
+
+ * Werkzeugleiste
+  * Navigationswerkzeuge
+   * Hereinzoomen über eine aufspannbaren Bereich
+   * Herauszoomen über einen statischen Faktor
+   * Auf Gesamtansicht zoomen
+   * Wertebereich in X- und Y-Richtung verschieben
+   * Anzeige der Cursorposition in der Einheit des Diagramms
+
+
+=== Datenkorb ===
+ * Intern
+  * Bereitstellung des Datentyps auf unterster Ebene zum Bereitstellung von "Früheren Berechnungen"
+
+
+=== Themenstil-Editor ===
+ * Intern: 
+  * Vorbereitungen getroffen, um Stile auf Diagramme graphisch anwenden zu können.
+
+
+=== Technische Aspekte ===
+ * Beschleunigung der jeweiligen Berechnungen
+ * Stabilität der Kommunikation von Client und Server verbessern.
+
+
+=== Offene Punkte ===
+ *  [[Flys-3.0/Version2.4Todo]].
+
+
+=== Liste aller bearbeiteten Issues ===
+
+{{{#!CSV ,
+"ID","Titel"
+10,"Fehler in FLYS-2.1 -> Anzeige Wertebereiche für Q am Pegel in FLYS-3 verschieden"
+17,"Verhalten der Strecken-/Ortauswahl im FLYS-WebClient"
+21,"Gleiches Projekt kann mehr als einmal gleichzeitig geöffnet werden"
+40,"WINFO: Vorbelegung nach Rücksprung in der Historie stellt ursprgl. gewählten Wert nicht mehr her"
+42,"Übergeordnete Navigation: Funktionen als Schaltflächen ausweisen"
+43,"i18n: Einige Verbesserungen"
+49,"Massenhafte Auswahl von Dauerzahlen als Berechnungsgrundlage von Wasserspiegellagen führt zu falschen Berechnungsergebnisse"
+55,"WINFO: Gewichtungsfaktor für Abflusstabellen in der Berechnung 'W für angepaßten Abfluss-Längsschnitt'"
+65,"Restlet akzeptiert keine weiteren Verbindungen"
+69,"Berechnungsart 1 'Wasserstand/Wasserspiegellage' fehlerhaft"
+76,"Importer: Lücken in den Q-Bereichen"
+77,"Diagramm: Beschriftung der Kurven bei Dauerlinien"
+81,"Rechenungenauigkeit Web-FLYS"
+82,"ArrayIndexOutOfBoundsException wenn Berechnungen fehlschlagen"
+84,"Kann nicht auf die erste Q-Spalte der Saar zugreifen."
+85,"WINFO: Statt an einzelnen Orten wird eine Strecke gerechnet"
+87,"WINFO-W/Wspl-Ort-gleichwertiger-Q"
+89,"WINFO, Dauerlinie, erst ab D=1 rechnen"
+90,"Diagramm: Trennung derDiagrammfläche und Achsenaufheben"
+91,"WINFO: Vorbelegung Q statt W"
+92,"WINFO: Saar-km 0.1-93 NQ HQ200 liefert keine Antwort"
+93,"WINFO: Benennung der Berechnungsart korrigieren"
+98,"W-INFO: Wasserspiegellagenberechnung und Auswahl über Q/W/D-Auswahl"
+99,"WINFO: Wasserspiegellagenberechnung ohne Auswahlunterstützung"
+100,"W-INFO: Wasserspiegellagenberechnung und veränderte Berechnungsrichtung"
+101,"W-INFO: Wasserspiegellagenberechnung / Strecke"
+102,"W-INFO: Wasserspiegellagenberechnung / Strecke - Probleme beim oberen und unteren Rand"
+103,"WINFO: Wasserspiegellagenberechnung / Layout-Inkonsistenz"
+105,"W-INFO: Wasserspiegellagenberechnung / Strecke und Bandbreite Q (Stabilitätsproblem)"
+106,"W-INFO: Wasserspiegellagenberechnung / Strecke + W am Pegel (Vertauschen von Werten)"
+108,"W-INFO: Wasserspiegellagenberechnung / W am Pegel (untere und obere Grenze)"
+109,"W-INFO: Wasserspiegellagenberechnung / Strecke + W am Pegel (fehlerhafte Berechnung)"
+111,"W-INFO: Wasserspiegellagenberechnung / Strecke"
+112,"Berechnungsergebnis und Export verschieden"
+116,"W-INFO: Wasserspiegellagenberechnung / Ort + Q-Auswahlunterstützung (Berechnungsergebnis bei punkthafter Berechnung fehlerhaft)"
+117,"W-INFO: Wasserspiegellagenberechnung / Q-Eingabe"
+118,"W-INFO: Eingabe Q aus Auswahlunterstützung und Manuell"
+119,"W-INFO: Wasserspiegellagenberechnung / Dateneingabe ausserhalb des Wertebereichs"
+122,"W-INFO: Wasserspiegellagenberechnung / Auswahlunterstützung Abfluss (W-Werte sind nicht auswählbar)"
+128,"WINFO: Anzeige von gewählten Orten und Strecken"
+129,"WINFO: Dauerlinie bei D=0"
+131,"Anzeige der Position bei großen Zahlen ungenau"
+132,"WINFO: Streckenauswahl per default setzen"
+134,"WINFO: Tabellarische Ausgabe ('Berechnungsausgabe') bei Abflusskurven"
+139,"WINFO: NPE bei Step-Back bei ungleichwertiger Berechnungsart"
+140,"WINFO: WQ-Tabelle in Tabs aufsplitten"
+142,"WINFO: Streckenauswahl: Oberkante/Unterkante hinzufügen"
+147,"WINFO: Ort der Änderung des gleichwertigen Abflusses falsch"
+149,"WINFO: Abflusskurve Mosel 242 wird an 1.904 km berechnet/gezeigt"
+153,"Abflusskurven am Pegel über Facetten"
+154,"Berechnete Abflusskurve/Abflusstafel zeigt keine Berechnungsergebnisse"
+156,"WINFO: Platzierung des Berechnungsberichtes"
+157,"Diagramm: Ursprung berechnete Abflusskurve"
+161,"Diagramm: Q-Achse in W-Längsschnitten immer bei Q=0"
+162,"WINFO: Kurzfassung von W/Q-Daten nicht vollständig"
+164,"Berechnung 4: Umgekehrtes Berechnungsintervall führt zur"
+165,"Projektliste: Einige Auffälligkeiten nach Neuimplementierung"
+169,"WINFO: WSP kann nicht durchgeführt werden"
+170,"Diagramm: Initiales Zoomen zeigt verschobenen Bereich"
+172,"Diagramm: Ursprung der Diagramme bei Dauerzahlen"
+173,"WINFO: W für ungleichwertigen Q-Längsschnitt: Abflussverteilung überschreitet obere Grenze"
+174,"Diagramm: Q-Linie wird bei initialem Laden des Diagramms anders dargestellt als bei Ansicht auf gesamten Wertebereich"
+175,"WINFO: Q um 7 qm/s zu hoch"
+182,"WINFO: Vorbelegung Q bei 'ungleichwertigem Abflusslängsschnitt'"
+}}}
+
+
+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:51 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:51 2012 +0200
@@ -0,0 +1,182 @@
+<?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.3.0</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.5</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>
+    <dependency>
+      <groupId>org.gwtopenmaps.openlayers</groupId>
+      <artifactId>gwt-openlayers-client</artifactId>
+      <version>0.6</version>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.14</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:51 2012 +0200
@@ -0,0 +1,45 @@
+<?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 Theme.                -->
+  <inherits name="com.smartgwt.SmartGwtNoTheme" />
+  <inherits name="com.smartclient.theme.enterprise.Enterprise"/>
+  <inherits name="com.smartclient.theme.enterprise.EnterpriseResources"/>
+
+  <!-- Does not work
+  <inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlue"/>
+  <inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlueResources"/>
+  -->
+
+  <!-- Works:
+  <inherits name="com.smartclient.theme.graphite.Graphite"/>
+  <inherits name="com.smartclient.theme.graphite.GraphiteResources"/>
+  -->
+
+  <!-- Inherit the gwtopenlayers stuff -->
+  <inherits name="org.gwtopenmaps.openlayers.OpenLayers"/>
+
+  <!-- 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:51 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:51 2012 +0200
@@ -0,0 +1,459 @@
+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.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);
+
+        vertical.draw();
+
+        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;
+
+                    if (collection.getItemLength() == 0) {
+                        CollectionView view = new CollectionView(
+                            FLYS.this, collection, null);
+
+                        view.addCollectionChangeHandler(
+                            getProjectList());
+                        view.addCloseClickHandler(
+                            new CloseCollectionViewHandler(
+                                FLYS.this, collectionID));
+
+                        workspace.addView(collectionID, view);
+
+                        return;
+                    }
+
+                    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, null,
+            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:51 2012 +0200
@@ -0,0 +1,477 @@
+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 clone_project();
+
+    String copy_of ();
+
+    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 empty_filter();
+
+    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 zoom_back();
+
+    String pan();
+
+    String fix();
+
+    String next();
+
+    String river_km();
+
+    String uesk_profile_distance();
+
+    String location_distance_state();
+
+    String distance_state();
+
+    String location();
+
+    String locations();
+
+    String distance();
+
+    String unitFrom();
+
+    String unitTo();
+
+    String dpLabelFrom();
+
+    String dpUnitFrom();
+
+    String dpLabelTo();
+
+    String dpUnitTo();
+
+    String dpLabelStep();
+
+    String dpUnitStep();
+
+    String wgLabelFrom();
+
+    String wgUnitFrom();
+
+    String wgLabelTo();
+
+    String wgUnitTo();
+
+    String wgLabelStep();
+
+    String wgUnitStep();
+
+    String unitWidth();
+
+    String unitFromInM();
+
+    String unitToInM();
+
+    String unitDiffInM();
+
+    String unitLocation();
+
+    String wrongFormat();
+
+    String too_many_values ();
+
+    String description();
+
+    String from();
+
+    String to();
+
+    String riverside();
+
+    String calcTableTitle();
+
+    String helperPanelTitle();
+
+    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();
+
+    String search();
+
+    String properties();
+
+    String activateTheme();
+
+    String deactivateTheme();
+
+    String removeTheme();
+
+    String label_ok();
+
+    String label_cancel();
+
+    // Elevation window
+
+    String ele_window_title();
+
+    String ele_window_label();
+
+    String ele_window_x_col();
+
+    String ele_window_y_col();
+
+    String ele_window_z_col();
+
+    String ele_window_ok_button();
+
+    String ele_window_cancel_button();
+
+    String ele_window_format_error();
+
+    String ele_window_save_error();
+
+    String ele_window_geometry_error();
+
+    // 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();
+
+    String floodmap();
+
+    String cross_section();
+
+    String w_differences();
+
+    // 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 w_differences_export();
+
+    String csv();
+
+    String wst();
+
+    String at();
+
+    String chart_themepanel_header_themes();
+
+    String chart_themepanel_header_actions();
+
+    String discharge();
+
+    // 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_remove_artifact();
+
+    String error_create_collection();
+
+    String error_describe_collection();
+
+    String error_no_rivers_found();
+
+    String error_no_such_user();
+
+    String error_no_waterlevel_pair_selected();
+
+    String error_same_waterlevels_in_pair();
+
+    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 error_load_artifact();
+
+    String error_no_calc_result();
+
+    String error_no_theme_styles_found();
+
+    String error_no_feature_selected();
+
+    String error_no_map_config();
+
+    String warning_use_first_feature();
+
+
+
+    String bottom_edge();
+
+    String top_edge();
+
+    // MAP RELATED STRINGS
+
+    String digitize();
+
+    String pipe1();
+
+    String pipe2();
+
+    String ditch();
+
+    String dam();
+
+    String ring_dike();
+
+    String selectFeature();
+
+    String removeFeature();
+
+    String adjustElevation();
+
+    String adjustElevationTooltip();
+
+    String measureLine();
+
+    String measurePolygon();
+
+    String step();
+
+    String calculationStarted();
+
+    String zoomMaxExtent();
+
+    String zoomIn();
+
+    String zoomOut();
+
+    String zoomLayer();
+
+    String moveMap();
+
+    String digitizeObjects();
+
+    String selectObject();
+
+    String removeObject();
+
+    String measureDistance();
+
+    String measureArea();
+
+    String map_themepanel_header_style();
+
+    String zoomToLayer();
+
+    // data cage
+
+    String old_calculations();
+
+    String datacageAdd();
+
+    String fixations();
+
+    String basedata();
+
+    String heightmarks();
+
+    String additionals();
+
+    String differences();
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,243 @@
+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
+clone_project = Clone Project
+copy_of = Copy
+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...
+empty_filter = No data found.
+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]
+distance_state = Choose calculation range [km]
+location = Location(s)
+locations = Locations
+distance = Range
+unitFrom = km
+unitTo = km a
+unitWidth = m
+search = Search
+discharge = FIXME(Name)
+properties = Properties
+activateTheme = Activate
+deactivateTheme = Deactivate
+removeTheme = Remove
+label_ok = Ok
+label_cancel = Cancel
+
+dpLabelFrom = From
+dpUnitFrom = km
+dpLabelTo = To
+dpUnitTo = km
+dpLabelStep = a
+dpUnitStep = m
+
+wgLabelFrom = From
+wgUnitFrom = m
+wgLabelTo = To
+wgUnitTo = m
+wgLabelStep = a
+wgUnitStep = m
+
+unitFromInM = From [m]:
+unitToInM = To [m]:
+unitDiffInM = Diff [m]:
+unitLocation = km
+wrongFormat = Wrong format
+too_many_values = Only one value allowed
+
+description = Description
+from = From
+to = To
+riverside = Riverside
+bottom_edge = Bottom edge
+top_edge = Top edge
+name = Name
+type = Type
+wq_value = Q/D
+river_km = River-Km
+uesk_profile_distance = Distance [m]
+
+wqTitle = Input for W/Q Data
+wqadaptedTitle = Input for W/Q Data
+calcTableTitle = Calculated Output
+helperPanelTitle = Input Support
+wqW = W at Gauge [cm]
+wqQ = Q [m\u00b3/s]
+wqQGauge = Discharge 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
+zoom_back = images/zoom-back.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
+floodmap = Floodmap
+
+cross_section = Cross Section
+w_differences = Differences
+w_differences_export = Differences Export
+
+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
+chart_themepanel_header_actions = Actions
+
+ele_window_title = Elevation Node
+ele_window_label = Elevation settings in m \u00fc. NN.
+ele_window_x_col = X
+ele_window_y_col = Y
+ele_window_z_col = Z-Coordinate
+ele_window_ok_button = Set elevations
+ele_window_cancel_button = Cancel
+ele_window_format_error = The elevation you have entered is invalid:
+ele_window_save_error = Error while saving elevations.
+ele_window_geometry_error = The geometry is not supported:
+
+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_remove_artifact = Error while removing 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_no_waterlevel_pair_selected = Error - no waterlevel pair for building differences selected.
+error_same_waterlevels_in_pair = Error - minuend and subtrahend have to differ.
+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.
+error_load_artifact = Error while adding data to the current project.
+error_no_calc_result=No calculation results found.
+error_no_theme_styles_found=No style for the selected theme found.
+error_no_feature_selected = No object selected.
+error_no_map_config = No Map configuration found.
+warning_use_first_feature = You have more that one object selected. The first one is used.
+
+## map related strings
+digitize = images/digitize.png
+pipe1 = Pipe1
+pipe2 = Pipe12
+ditch = Ditch
+dam = Dam
+ring_dike = Ringdike
+selectFeature = images/selectFeature.png
+removeFeature = images/removeFeature.png
+adjustElevation = images/adjustElevation.png
+measureLine = images/measureLine.png
+measurePolygon = images/measurePolygon.png
+step = Step
+calculationStarted = Calculation started.
+zoomMaxExtent = Max extent
+zoomIn = Zoom in
+zoomOut = Zoom out
+zoomLayer = Zoom to layer
+moveMap = Move
+digitizeObjects = Creation of pipes, ditches, dams and ringdikes
+selectObject = Select tool
+removeObject = Remove selected object
+adjustElevationTooltip = Define elevations for barriers
+measureDistance = Measure line
+measureArea = Measure area
+map_themepanel_header_style = Style
+zoomToLayer = Layer-Zoom
+
+# data cage
+old_calculations = Fr\u00fchere Berechnungen
+datacageAdd = Daten laden
+heightmarks = Height Marks
+basedata = Basis-Daten
+fixations = Fixierungen
+additionals = Additional Longitudinal Section Curves
+differences = Differenzen
--- /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:51 2012 +0200
@@ -0,0 +1,243 @@
+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
+clone_project = Projekt duplizieren
+copy_of = Kopie
+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...
+empty_filter = Keine Daten gefunden.
+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]
+distance_state = Berechnungsstrecke w\u00e4hlen [km]
+location = Ort(e)
+locations = Orte
+distance = Strecke
+unitFrom = km&nbsp;-
+unitTo = km a
+unitWidth = m
+search = Suchbegriff
+discharge = Kennzeichnender Abfluss
+properties = Eigenschaften
+activateTheme = Aktivieren
+deactivateTheme = Deaktivieren
+removeTheme = Entfernen
+label_ok = Ok
+label_cancel = Abbrechen
+
+dpLabelFrom = Von
+dpUnitFrom = km
+dpLabelTo = Bis
+dpUnitTo = km
+dpLabelStep = a
+dpUnitStep = m
+
+wgLabelFrom = Von
+wgUnitFrom = m
+wgLabelTo = Bis
+wgUnitTo = m
+wgLabelStep = a
+wgUnitStep = m
+
+unitFromInM = Von [m]:
+unitToInM = Bis [m]:
+unitDiffInM = Diff [m]:
+unitLocation = km
+wrongFormat = Falsches Format
+too_many_values = Nur ein Eingabewert erlaubt
+
+description = Beschreibung
+from = von
+to = bis
+bottom_edge = Unterkante
+top_edge = Oberkante
+riverside = Flussseite
+name = Name
+type = Typ
+wq_value = Q/D
+river_km = Fluss-Km
+uesk_profile_distance = Abstand [m]
+
+wqTitle = Eingabe f\u00fcr W/Q Daten
+wqadaptedTitle = Eingabe f\u00fcr W/Q Daten
+calcTableTitle = Berechnungsausgabe
+helperPanelTitle = Eingabeunterst\u00fctzung
+wqW = W am Pegel [cm]
+wqQ = Q [m\u00b3/s]
+wqQGauge = Kennzeichnender Abfluss am Pegel
+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
+zoom_back = images/zoom-back.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
+floodmap = \u00dcberschwemmungsfl\u00e4che
+
+cross_section = Querprofil
+w_differences = Differenzen
+w_differences_export = Differenzen Export
+
+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
+chart_themepanel_header_action = Aktionen
+
+ele_window_title = H\u00f6henknoten setzen
+ele_window_label = H\u00f6henangaben in m \u00fc. NN.
+ele_window_x_col = X
+ele_window_y_col = Y
+ele_window_z_col = Z-Koordinate
+ele_window_ok_button = H\u00f6he setzen
+ele_window_cancel_button = Abbrechen
+ele_window_format_error = Die von Ihnen eigegebene H\u00f6he ist ung\u00fcltig:
+ele_window_save_error = Fehler beim Speichern der H\u00f6hen.
+ele_window_geometry_error = Die Geometrie wird nicht unterst\u00fctzt:
+
+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 einer Parametrisierung.
+error_remove_artifact = Fehler beim Entfernen einer Parametrisierung.
+error_create_collection = Fehler beim Erstellen eines neuen Projektes.
+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_no_waterlevel_pair_selected = Fehler - kein Paar zur Differenzbuilding gew\u00e4hlt.
+error_same_waterlevels_in_pair = Error - minuend and subtrahend m\u00fcssen sich unterscheiden.
+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.
+error_load_artifact = Fehler beim Hinzu\u00fcgen neuer Daten in das aktuelle Projekt.
+error_no_calc_result=Es wurden keine Berechnungsergebnisse gefunden.
+error_no_theme_styles_found=Es konnten keine Stile f\u00fcr das gew\u00e4hlte Thema gefunden werden.
+error_no_feature_selected = Kein Objekt ausgew\u00e4hlt.
+error_no_map_config = Es konnte keine Kartenkonfiguration gefunden werden.
+warning_use_first_feature = Sie haben mehr als ein Objekt ausgew\u00e4hlt. Das erste Objekt wird benutzt.
+
+## map related strings
+digitize = images/digitize.png
+pipe1 = Rohr1
+pipe2 = Rohr2
+ditch = Graben
+dam = Damm
+ring_dike = Ringdeich
+selectFeature = images/selectFeature.png
+removeFeature = images/removeFeature.png
+adjustElevation = images/adjustElevation.png
+measureLine = images/measureLine.png
+measurePolygon = images/measurePolygon.png
+step = Schritt
+calculationStarted = Berechnung wurde gestarted.
+zoomMaxExtent = Gesamtausschnitt
+zoomIn = Hereinzoomen
+zoomOut = Herauszoomen
+zoomLayer = Zoom auf Layer
+moveMap = Verschieben
+digitizeObjects = Erstellung von Rohren, Gr\u00e4ben, D\u00e4mmen und Ringdeichen
+selectObject = Auswahlwerkzeug
+removeObject = Ausgew\u00e4hltes Objekt l\u00f6schen
+adjustElevationTooltip = Festlegen von H\u00f6hen f\u00fcr D\u00e4mme, Ringeiche, Gr\u00e4ben und Rohre
+measureDistance = L\u00e4ngenmessung
+measureArea = Streckenmessung
+map_themepanel_header_style = Stil
+zoomToLayer = Layer-Zoom
+
+# data cage
+old_calculations = Fr\u00fchere Berechnungen
+datacageAdd = Daten laden
+heightmarks = Hochwassermarken
+basedata = Basis-Daten
+fixations = Fixierungen
+additionals = Zus\u00e4tzliche L\u00e4ngsschnitte
+differences = Differenzen
--- /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:51 2012 +0200
@@ -0,0 +1,243 @@
+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
+clone_project = Clone Project
+copy_of = Copy
+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...
+empty_filter = No data found.
+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]
+distance_state = Choose calculation range [km]
+location = Location(s)
+locations = Locations
+distance = Range
+unitFrom = km
+unitTo = km a
+unitWidth = m
+search = Search
+discharge = FIXME(Name)
+properties = Properties
+activateTheme = Activate
+deactivateTheme = Deactivate
+removeTheme = Remove
+label_ok = Ok
+label_cancel = Cancel
+
+dpLabelFrom = From
+dpUnitFrom = km
+dpLabelTo = To
+dpUnitTo = km
+dpLabelStep = a
+dpUnitStep = m
+
+wgLabelFrom = From
+wgUnitFrom = m
+wgLabelTo = To
+wgUnitTo = m
+wgLabelStep = a
+wgUnitStep = m
+
+unitFromInM = From [m]:
+unitToInM = To [m]:
+unitDiffInM = Diff [m]:
+unitLocation = km
+wrongFormat = Wrong format
+too_many_values = Only one value allowed
+
+description = Description
+from = From
+to = To
+riverside = Riverside
+bottom_edge = Bottom edge
+top_edge = Top edge
+name = Name
+type = Type
+wq_value = Q/D
+river_km = River-Km
+uesk_profile_distance = Distance [m]
+
+wqTitle = Input for W/Q Data
+wqadaptedTitle = Input for W/Q Data
+calcTableTitle = Calculated Output
+helperPanelTitle = Input Support
+wqW = W at Gauge [cm]
+wqQ = Q [m\u00b3/s]
+wqQGauge = Discharge 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
+zoom_back = images/zoom-back.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
+floodmap = Floodmap
+
+cross_section = Cross Section
+w_differences = Differences
+w_differences_export = Differences Export
+
+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
+chart_themepanel_header_actions = Actions
+
+ele_window_title = Elevation Node
+ele_window_label = Elevation settings in m \u00fc. NN.
+ele_window_x_col = X
+ele_window_y_col = Y
+ele_window_z_col = Z-Coordinate
+ele_window_ok_button = Set elevations
+ele_window_cancel_button = Cancel
+ele_window_format_error = The elevation you have entered is invalid:
+ele_window_save_error = Error while saving elevations.
+ele_window_geometry_error = The geometry is not supported:
+
+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_remove_artifact = Error while removing 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_no_waterlevel_pair_selected = Error - no waterlevel pair for building differences selected.
+error_same_waterlevels_in_pair = Error - minuend and subtrahend have to differ.
+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.
+error_load_artifact = Error while adding data to the current project.
+error_no_calc_result=No calculation results found.
+error_no_theme_styles_found=No style for the selected theme found.
+error_no_feature_selected = No object selected.
+error_no_map_config = No Map configuration found.
+warning_use_first_feature = You have more that one object selected. The first one is used.
+
+## map related strings
+digitize = images/digitize.png
+pipe1 = Pipe1
+pipe2 = Pipe12
+ditch = Ditch
+dam = Dam
+ring_dike = Ringdike
+selectFeature = images/selectFeature.png
+removeFeature = images/removeFeature.png
+adjustElevation = images/adjustElevation.png
+measureLine = images/measureLine.png
+measurePolygon = images/measurePolygon.png
+step = Step
+calculationStarted = Calculation started.
+zoomMaxExtent = Max extent
+zoomIn = Zoom in
+zoomOut = Zoom out
+zoomLayer = Zoom to layer
+moveMap = Move
+digitizeObjects = Creation of pipes, ditches, dams and ringdikes
+selectObject = Select tool
+removeObject = Remove selected object
+adjustElevationTooltip = Define elevations for barriers
+measureDistance = Measure line
+measureArea = Measure area
+map_themepanel_header_style = Style
+zoomToLayer = Layer-Zoom
+
+# data cage
+old_calculations = Fr\u00fchere Berechnungen
+datacageAdd = Daten laden
+heightmarks = Height Marks
+basedata = Basis-Daten
+fixations = Fixierungen
+additionals = Additional Longitudinal Section Curves
+differences = Differenzen
--- /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:51 2012 +0200
@@ -0,0 +1,40 @@
+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 ();
+
+    @Source("images/loading.gif")
+    ImageResource loading();
+
+    @Source("images/downloadPNG.png")
+    ImageResource downloadPNG();
+
+    @Source("images/downloadPDF.png")
+    ImageResource downloadPDF();
+
+    @Source("images/downloadSVG.png")
+    ImageResource downloadSVG();
+}
--- /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:51 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:51 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/AdvanceHandler.java	Fri Sep 28 12:14:51 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 AdvanceHandler extends Serializable {
+
+    public void onAdvance(String 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/CollectionChangeEvent.java	Fri Sep 28 12:14:51 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:51 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/DatacageDoubleClickHandler.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,9 @@
+package de.intevation.flys.client.client.event;
+
+import de.intevation.flys.client.shared.model.ToLoad;
+
+
+public interface DatacageDoubleClickHandler {
+
+    void onDoubleClick(ToLoad toLoad);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/event/DatacageHandler.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,9 @@
+package de.intevation.flys.client.client.event;
+
+import de.intevation.flys.client.shared.model.ToLoad;
+
+public interface DatacageHandler
+{
+    void toLoad(ToLoad toLoad);
+}
+// vim:set ts=4 sw=4 si et sta 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/FilterHandler.java	Fri Sep 28 12:14:51 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 FilterHandler {
+
+    void onFilterCriteriaChanged(StringFilterEvent 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:51 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:51 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:51 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:51 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:51 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/HasRedrawRequestHandlers.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * Implements function to add a RedrawRequestHandler.
+ */
+public interface HasRedrawRequestHandlers {
+
+    /**
+     * Adds a new RedrawRequestHandler.
+     *
+     * @param handler The new RedrawRequestHandler
+     */
+    public void addRedrawRequestHandler(RedrawRequestHandler rrh);
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,48 @@
+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 {
+
+    public static enum Type {
+        FORWARD, BACK
+    }
+
+
+    protected Artifact oldArtifact;
+    protected Artifact newArtifact;
+
+    protected Type     type;
+
+
+    public ParameterChangeEvent(Artifact old, Artifact newArt) {
+        this(old, newArt, Type.FORWARD);
+    }
+
+
+    public ParameterChangeEvent(Artifact oArt, Artifact nArt, Type type) {
+        oldArtifact = oArt;
+        newArtifact = nArt;
+        this.type   = type;
+    }
+
+
+    public Artifact getOldValue() {
+        return oldArtifact;
+    }
+
+    public Artifact getNewValue() {
+        return newArtifact;
+    }
+
+    public Type 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/ParameterChangeHandler.java	Fri Sep 28 12:14:51 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/RedrawRequestEvent.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,28 @@
+package de.intevation.flys.client.client.event;
+
+
+public class RedrawRequestEvent {
+
+    public static enum Type {
+        RESET, DEFAULT
+    }
+
+
+    protected Type type;
+
+
+    public RedrawRequestEvent() {
+        this.type = Type.DEFAULT;
+    }
+
+
+    public RedrawRequestEvent(Type type) {
+        this.type = type;
+    }
+
+
+    public Type 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/RedrawRequestHandler.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,15 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * Handler interface for RedrawRequests.
+ */
+public interface RedrawRequestHandler {
+
+    /**
+     * Handle the redraw request.
+     */
+    public void onRedrawRequest(RedrawRequestEvent 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:51 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:51 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:51 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:51 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/StringFilterEvent.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,20 @@
+package de.intevation.flys.client.client.event;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StringFilterEvent {
+
+    protected String filter;
+
+
+    public StringFilterEvent(String filter) {
+        this.filter = filter;
+    }
+
+
+    public String getFilter() {
+        return filter;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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:51 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/downloadPDF.png has changed
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/downloadPNG.png has changed
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/downloadSVG.png 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/loading.gif 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:51 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:51 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:51 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:51 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:51 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.Recommendation;
+
+/**
+ * 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,
+        Recommendation recommendation
+    ) 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:51 2012 +0200
@@ -0,0 +1,24 @@
+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.Recommendation;
+
+/**
+ * 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,
+        Recommendation recommendation,
+        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:51 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:51 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:51 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:51 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:51 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:51 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/CollectionItemAttributeService.java	Fri Sep 28 12:14:51 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.Collection;
+import de.intevation.flys.client.shared.model.CollectionItemAttribute;
+
+/**
+ * This interface provides a method to retrieve an artifact based on its uuid.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+@RemoteServiceRelativePath("collection-item-attribute")
+public interface CollectionItemAttributeService extends RemoteService {
+
+    CollectionItemAttribute getCollectionItemAttribute(
+        Collection collection,
+        String artifact,
+        String url,
+        String locale)
+    throws ServerException;
+
+    void setCollectionItemAttribute(
+        Collection collection,
+        String artifact,
+        String url,
+        String locale,
+        CollectionItemAttribute attribute)
+    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/CollectionItemAttributeServiceAsync.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionItemAttribute;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface CollectionItemAttributeServiceAsync {
+
+    public void getCollectionItemAttribute(
+        Collection              collection,
+        String                  artifact,
+        String                  serverUrl,
+        String                  locale,
+        AsyncCallback<CollectionItemAttribute> callback);
+
+    public void setCollectionItemAttribute(
+        Collection              collection,
+        String                  artifact,
+        String                  serverUrl,
+        String                  locale,
+        CollectionItemAttribute attributes,
+        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/CreateCollectionService.java	Fri Sep 28 12:14:51 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:51 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:51 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:51 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/DescribeArtifactService.java	Fri Sep 28 12:14:51 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.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * This interface provides artifact specific operation DESCRIBE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("describe")
+public interface DescribeArtifactService extends RemoteService {
+
+    /**
+     * This method inserts new data into the an existing artifact.
+     *
+     * @param url The url of the artifact server.
+     * @param locale The locale used for the request.
+     * @param artifact The artifact.
+     *
+     * @return the artifact which description might have been changed.
+     */
+    public Artifact describe(
+        String   url,
+        String   locale,
+        Artifact artifact)
+    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/DescribeArtifactServiceAsync.java	Fri Sep 28 12:14:51 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;
+
+
+/**
+ * This interface provides artifact specific operation DESCRIBE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface DescribeArtifactServiceAsync {
+
+    public void describe(
+        String                  url,
+        String                  locale,
+        Artifact                artifact,
+        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/DescribeCollectionService.java	Fri Sep 28 12:14:51 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:51 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:51 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:51 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/FeedService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,33 @@
+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.
+ */
+@RemoteServiceRelativePath("feed")
+public interface FeedService extends RemoteService {
+
+    /**
+     * Inserts new data into 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 feed(
+        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/FeedServiceAsync.java	Fri Sep 28 12:14:51 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.Data;
+
+
+/**
+ * This interface provides artifact specific operation FEED.
+ */
+public interface FeedServiceAsync {
+
+    public void feed(
+        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/GetArtifactService.java	Fri Sep 28 12:14:51 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:51 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/LoadArtifactService.java	Fri Sep 28 12:14:51 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;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.Recommendation;
+
+
+@RemoteServiceRelativePath("load-artifact")
+public interface LoadArtifactService extends RemoteService {
+
+    Artifact load(
+        Collection     parent,
+        Recommendation recom,
+        String         factory,
+        String         url,
+        String         locale)
+    throws ServerException;
+
+    Artifact[] loadMany(
+        Collection       parent,
+        Recommendation[] recom,
+        String           factory,
+        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/LoadArtifactServiceAsync.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,31 @@
+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;
+import de.intevation.flys.client.shared.model.Recommendation;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface LoadArtifactServiceAsync {
+
+    public void load(
+        Collection     parent,
+        Recommendation recom,
+        String         factory,
+        String         url,
+        String         locale,
+        AsyncCallback<Artifact> callback);
+
+    public void loadMany(
+        Collection       parent,
+        Recommendation[] recom,
+        String           factory,
+        String           url,
+        String           locale,
+        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/MapInfoService.java	Fri Sep 28 12:14:51 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.MapInfo;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("map-info")
+public interface MapInfoService extends RemoteService {
+
+    MapInfo getMapInfo(String url, 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/MapInfoServiceAsync.java	Fri Sep 28 12:14:51 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.MapInfo;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface MapInfoServiceAsync {
+
+    public void getMapInfo(
+        String url,
+        String locale,
+        String river,
+        AsyncCallback<MapInfo> 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/MapOutputService.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,22 @@
+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;
+import de.intevation.flys.client.shared.model.MapConfig;
+
+
+/**
+ * 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("map")
+public interface MapOutputService extends RemoteService {
+
+    MapConfig doOut(Collection collection) 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/MapOutputServiceAsync.java	Fri Sep 28 12:14:51 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.Collection;
+import de.intevation.flys.client.shared.model.MapConfig;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface MapOutputServiceAsync {
+
+    public void doOut(Collection collection, AsyncCallback<MapConfig> 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:51 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 artifactId, String userId, String outs, String parameters
+    ) 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:51 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.DataCageTree;
+
+public interface MetaDataServiceAsync {
+    void getMetaData(
+        String                      serverUrl,
+        String                      locale,
+        String                      artifactId,
+        String                      userId,
+        String                      outs,
+        String                      parameters,
+        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/RemoveArtifactService.java	Fri Sep 28 12:14:51 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.Collection;
+
+
+/**
+ * This interface describes the service to remove an existing artifact to an
+ * existing collection.
+ *
+ */
+@RemoteServiceRelativePath("remove-artifact")
+public interface RemoveArtifactService extends RemoteService {
+
+    /**
+     * Removes an artifact from a collection.
+     *
+     * @param collection The Collection that should be modified.
+     * @param artifactId The artifact that should be removed.
+     * @param url        The url of the artifact server.
+     * @param locale     locae to use (for localized responses).
+     *
+     * @return the Collection after the operation.
+     */
+    Collection remove(
+        Collection collection,
+        String     artifactId,
+        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/RemoveArtifactServiceAsync.java	Fri Sep 28 12:14:51 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;
+
+/**
+ * @see RemoveArtifactService, RemoveArtifactServiceImpl
+ */
+public interface RemoveArtifactServiceAsync {
+
+    public void remove(
+        Collection collection,
+        String     artifactId,
+        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/ReportService.java	Fri Sep 28 12:14:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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:51 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.Data;
+
+/**
+ * This interface provides artifact specific operations FEED and ADVANCE.
+ *
+ * @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 and
+     * advances its state.
+     *
+     * @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:51 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 and ADVANCE.
+ *
+ * @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:51 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:51 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:51 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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,302 @@
+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.Button;
+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.layout.VLayout;
+
+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.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;
+
+/**
+ * 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 VLayout helperContainer;
+
+    /** The artifact that contains status information.*/
+    protected Artifact artifact;
+
+    /** The Collection.*/
+    protected Collection collection;
+
+    /** The ParameterList.*/
+    protected ParameterList parameterList;
+
+    /**
+     * 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 {
+            showErrors(errors);
+        }
+    }
+
+
+    protected void showErrors(List<String> errors) {
+        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(VLayout 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;
+    }
+
+
+    public void setCollection(Collection collection) {
+        this.collection = collection;
+    }
+
+
+    public void setParameterList(ParameterList list) {
+        this.parameterList = list;
+    }
+
+
+    public Collection getCollection() {
+        return collection;
+    }
+
+
+    /**
+     * 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 String getDataValue(String state, String name) {
+        ArtifactDescription desc = artifact.getArtifactDescription();
+
+        DataList[] old = desc.getOldData();
+
+        for (DataList list: old) {
+            Data d = getData(list.getAll(), name);
+
+            if (d != null) {
+                return d.getItems()[0].getStringValue();
+            }
+        }
+
+        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/AutoIntegerPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,119 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.validator.Validator;
+import com.smartgwt.client.widgets.form.validator.CustomValidator;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataList;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AutoIntegerPanel extends TextProvider {
+
+    public static final String FIELD_NAME = "integer_field";
+
+    public static final String FIELD_DEFAULT_VALUE = "auto";
+
+
+    protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected String dataName;
+
+
+    @Override
+    public Canvas create(DataList dataList) {
+        Data   data   = dataList.get(0);
+        this.dataName = data.getLabel();
+
+        Canvas label  = new Label(data.getDescription());
+        Canvas form   = createForm(getTitle());
+        Canvas submit = getNextButton();
+
+        VLayout layout = new VLayout();
+        layout.setHeight(35);
+        label.setHeight(35);
+
+        layout.addMember(label);
+        layout.addMember(form);
+        layout.addMember(submit);
+        layout.setMembersMargin(10);
+
+        initDefaultValues(dataList);
+
+        return layout;
+    }
+
+
+    @Override
+    protected void initDefaultValues(DataList dataList) {
+        super.initDefaultValues(dataList);
+
+        String def = getValueAsString();
+
+        if (def == null || def.length() == 0) {
+            form.setValue(getFieldName(), FIELD_DEFAULT_VALUE);
+        }
+    }
+
+
+    protected String getTitle() {
+        return MSG.uesk_profile_distance();
+    }
+
+
+    @Override
+    protected String getDataName() {
+        return dataName;
+    }
+
+
+    @Override
+    protected String getValueAsString() {
+        String v = (String) form.getValue(getFieldName());
+        return v.toLowerCase();
+    }
+
+
+    @Override
+    protected Validator getValidator() {
+        Validator v = new AutoIntegerValidator();
+        v.setValidateOnChange(false);
+
+        return v;
+    }
+
+
+    public class AutoIntegerValidator extends CustomValidator {
+        @Override
+        protected boolean condition(Object value) {
+            String v = (String) value;
+
+            if (v == null || v.length() == 0) {
+                return false;
+            }
+
+            if (v.trim().equalsIgnoreCase("auto")) {
+                return true;
+            }
+
+            try {
+                Integer anInt = Integer.parseInt(v);
+
+                return true;
+            }
+            catch (NumberFormatException nfe) {
+                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-client/src/main/java/de/intevation/flys/client/client/ui/BooleanPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,99 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.types.FieldType;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+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;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class BooleanPanel extends TextProvider {
+
+    public static final String FIELD_NAME  = "boolean_field";
+    public static final int    TITLE_WIDTH = 0;
+
+    protected String dataName;
+
+
+    @Override
+    public Canvas create(DataList dataList) {
+        Data       data  = dataList.get(0);
+        DataItem[] items = data.getItems();
+
+        this.dataName = data.getLabel();
+
+        VLayout layout = new VLayout();
+        Label   label  = new Label(data.getDescription());
+        Canvas  form   = createForm(getTitle(items[0]));
+
+        layout.setMembersMargin(10);
+        layout.setHeight(35);
+        label.setHeight(35);
+
+        layout.addMember(label);
+        layout.addMember(form);
+        layout.addMember(getNextButton());
+        layout.setMembersMargin(10);
+
+        initDefaultValues(dataList);
+
+        return layout;
+    }
+
+
+    protected String getTitle(DataItem item) {
+        return item.getLabel();
+    }
+
+
+    @Override
+    protected void initDefaultValues(DataList dataList) {
+        Data     data = dataList.get(0);
+        DataItem item = data.getDefault();
+
+        String value = item.getStringValue();
+        Boolean bool = Boolean.valueOf(value);
+
+        if (bool) {
+            form.setValue(getFieldName(), bool);
+        }
+    }
+
+
+    @Override
+    protected FieldType getFieldType() {
+        return FieldType.BOOLEAN;
+    }
+
+
+    @Override
+    protected String getFieldName() {
+        return FIELD_NAME;
+    }
+
+
+    @Override
+    protected String getDataName() {
+        return dataName;
+    }
+
+
+    @Override
+    protected String getValueAsString() {
+        Boolean aBool = (Boolean) form.getValue(getFieldName());
+
+        return aBool != null ? aBool.toString() : "false";
+    }
+
+
+    @Override
+    protected int getTitleWidth() {
+        return TITLE_WIDTH;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,678 @@
+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 java.util.Stack;
+
+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 com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
+
+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.Recommendation;
+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.ArtifactService;
+import de.intevation.flys.client.client.services.ArtifactServiceAsync;
+import de.intevation.flys.client.client.services.AddArtifactService;
+import de.intevation.flys.client.client.services.AddArtifactServiceAsync;
+import de.intevation.flys.client.client.services.CollectionAttributeService;
+import de.intevation.flys.client.client.services.CollectionAttributeServiceAsync;
+import de.intevation.flys.client.client.services.CreateCollectionService;
+import de.intevation.flys.client.client.services.CreateCollectionServiceAsync;
+import de.intevation.flys.client.client.services.DescribeCollectionService;
+import de.intevation.flys.client.client.services.DescribeCollectionServiceAsync;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionView
+extends      Window
+implements   CollectionChangeHandler, HasCollectionChangeHandlers,
+             OutputModesChangeHandler, HasOutputModesChangeHandlers,
+             ParameterChangeHandler, CloseClickHandler
+{
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected CreateCollectionServiceAsync createCollectionService =
+        GWT.create(CreateCollectionService.class);
+
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected ArtifactServiceAsync createArtifactService =
+        GWT.create(ArtifactService.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);
+
+    protected CollectionAttributeServiceAsync updater =
+        GWT.create(CollectionAttributeService.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 CollectionChangeHandlers. */
+    protected List<CollectionChangeHandler> handlers;
+
+    /** The list of OutputModesChangeHandlers. */
+    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;
+
+    protected int artifactsQueue;
+    protected Stack<Recommendation> newRecommendations;
+
+
+    /**
+     * 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) {
+        // do this first because it takes longer than the other stuff in here.
+        User user = flys.getCurrentUser();
+        createNewCollection(user.identifier());
+
+        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());
+        this.artifactsQueue     = 0;
+        this.newRecommendations = new Stack<Recommendation>();
+
+        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.artifact      = artifact;
+        this.collection    = collection;
+        this.tabs          = new TabSet();
+        this.outputTabs    = new HashMap<String, OutputTab>();
+        this.handlers      = new ArrayList<CollectionChangeHandler>();
+        this.outHandlers   = new ArrayList<OutputModesChangeHandler>();
+        this.layout        = new VLayout();
+
+        if (artifact != null) {
+            this.parameterList = new ParameterList(
+                flys,
+                this,
+                messages.getString(artifact.getName()),
+                artifact);
+        }
+        else {
+            this.parameterList = new ParameterList(
+                flys, this, messages.new_project());
+        }
+
+        this.artifactsQueue     = 0;
+        this.newRecommendations = new Stack<Recommendation>();
+
+        addCollectionChangeHandler(this);
+        addCollectionChangeHandler(parameterList);
+        addCollectionChangeHandler(flys);
+        addOutputModesChangeHandler(this);
+        addOutputModesChangeHandler(parameterList);
+        addCloseClickHandler(this);
+
+        parameterList.addParameterChangeHandler(this);
+
+        init();
+
+        setCollection(collection);
+
+        if (artifact != null) {
+            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.");
+                    setCollection(collection);
+                }
+            });
+    }
+
+
+    protected FLYS getFlys() {
+        return flys;
+    }
+
+
+    /**
+     * This method registers a new CollectionChangeHandler.
+     *
+     * @param handler The new CollectionChangeHandler.
+     */
+    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;
+    }
+
+
+    public User getUser() {
+        return getFlys().getCurrentUser();
+    }
+
+
+    /**
+     * Set the current artifact that is the master of the parameterization.
+     *
+     * @param artifact The new artifact.
+     */
+    public void setArtifact(Artifact artifact) {
+        this.artifact = artifact;
+
+        onArtifactChanged(artifact);
+    }
+
+
+    public void onArtifactChanged(Artifact artifact) {
+        artifactChanged();
+
+        if (artifact.isInBackground()) {
+            new LoadingPanel(this, 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");
+        setArtifact(event.getNewValue());
+    }
+
+
+    protected void artifactChanged() {
+        ArtifactDescription desc = getArtifact().getArtifactDescription();
+        OutputMode[] outs        = desc.getOutputModes();
+        Recommendation[] recom   = desc.getRecommendations();
+
+        Collection c = getCollection();
+
+        if (recom != null && collection != null) {
+            loadRecommendedArtifacts(recom);
+        }
+
+        if (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;
+    }
+
+
+    protected void setCollection(Collection collection) {
+        setCollection(collection, false);
+    }
+
+
+    /**
+     * Set the current collection.
+     *
+     * @param collection The new collection.
+     */
+    protected void setCollection(Collection collection, boolean suppress) {
+        if (collection != null && this.collection == null) {
+            flys.getWorkspace().addView(collection.identifier(), this);
+        }
+
+        Collection tmp  = this.collection;
+        this.collection = collection;
+
+        setTitle(collection.getName() + " (UUID: " + collection.identifier() + ")");
+
+        if (!suppress) {
+            fireCollectionChangeEvent(tmp, this.collection);
+        }
+    }
+
+
+    public void onCollectionChange(CollectionChangeEvent event) {
+        if (artifactsQueue > 0) {
+            GWT.log("Do not update UI because we are still loading Artifacts.");
+            return;
+        }
+
+        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) {
+        clearOutputTabs();
+        OutputMode[] outs = event.getOutputModes();
+
+        if (outs == null) {
+            return;
+        }
+
+        boolean hasCSV = false;
+
+        for (OutputMode out: outs) {
+            addOutputTab(out.getName(), out);
+
+            if (out instanceof ExportMode) {
+                ExportMode export = (ExportMode) out;
+
+                if (export.getFacet("csv") != null) {
+                    hasCSV = true;
+                }
+            }
+        }
+
+        if (!hasCSV) {
+            parameterList.removeTable();
+        }
+    }
+
+
+    /**
+     * 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 && !parameterList.hasTable()) {
+                TableDataPanel p = new TableDataPanel();
+                p.setUuid(collection.identifier());
+                p.setName(out.getName());
+                parameterList.setTable(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 + "'");
+
+        String title  = messages.getString(name);
+        OutputTab tab = out.createOutputTab(title, getCollection(), this);
+
+        if (tab != null) {
+            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();
+        }
+    }
+
+
+    public void addArtifactToCollection(final Artifact artifact) {
+        Config config               = Config.getInstance();
+        final String url            = config.getServerUrl();
+        final String locale         = config.getLocale();
+        final Collection collection = getCollection();
+
+        artifactsQueue++;
+
+        addArtifactService.add(
+            collection, artifact, url, locale,
+            new AsyncCallback<Collection>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("An error occured while adding artifact.");
+                    artifactsQueue--;
+                    SC.warn(messages.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Collection newCollection) {
+                    GWT.log("Successfully added artifact.");
+                    setCollection(newCollection);
+
+                    artifactsQueue--;
+                    addRecommendationsToCollection();
+                }
+            }
+        );
+    }
+
+
+    protected void addRecommendationsToCollection() {
+        Config config               = Config.getInstance();
+        final String url            = config.getServerUrl();
+        final String locale         = config.getLocale();
+        final Collection collection = getCollection();
+
+        collection.addRecommendations(newRecommendations);
+
+        updater.update(collection, url, locale,
+            new AsyncCallback<Collection>() {
+                public void onFailure(Throwable caught) {
+                    newRecommendations.removeAllElements();
+                    setCollection(collection);
+
+                    GWT.log("An error occured while saving recommendations.");
+                    // TODO POPUP WARNING
+                }
+
+                public void onSuccess(Collection newCol) {
+                    GWT.log("Successfully saved recommendations.");
+                    newRecommendations.removeAllElements();
+                    setCollection(newCol);
+                }
+            }
+        );
+    }
+
+
+    protected void loadRecommendedArtifacts(Recommendation[] recommendations) {
+        Config config               = Config.getInstance();
+        final String url            = config.getServerUrl();
+        final String locale         = config.getLocale();
+        final Collection collection = getCollection();
+
+
+        Artifact masterArtifact = getArtifact();
+
+
+        if (recommendations == null) {
+            GWT.log("WARNING: Currently no recommendations.");
+            return;
+        }
+
+        for (final Recommendation recommendation: recommendations) {
+            if (collection.loadedRecommendation(recommendation)) {
+                continue;
+            }
+
+            // XXX: UGLY! If no reference artifact given use uuid of
+            //      current artifact as reference.      
+            if (recommendation.getMasterArtifact() == null) {
+                recommendation.setMasterArtifact(masterArtifact.getUuid());
+            }
+
+            final String factory = recommendation.getFactory();
+
+            GWT.log("Load recommended artifact with factory: " + factory);
+
+            createArtifactService.create(
+                url, locale, factory, recommendation,
+                new AsyncCallback<Artifact>() {
+                    public void onFailure(Throwable caught) {
+                        GWT.log("Error loading recommendations: " +
+                            caught.getMessage());
+                    }
+
+                    public void onSuccess(Artifact artifact) {
+                        GWT.log("Created new artifact: " + artifact.getUuid());
+                        addArtifactToCollection(artifact);
+                        newRecommendations.push(recommendation);
+                    }
+            });
+        }
+    }
+
+
+    public void registerTabHandler(TabSelectedHandler tse) {
+        tabs.addTabSelectedHandler(tse);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/ContinuePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,90 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.LinkItem;
+import com.smartgwt.client.widgets.form.fields.events.ClickEvent;
+import com.smartgwt.client.widgets.form.fields.events.ClickHandler;
+
+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.client.FLYSConstants;
+import de.intevation.flys.client.client.event.AdvanceHandler;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ContinuePanel extends AbstractUIProvider {
+
+    protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected List<AdvanceHandler> advHandlers;
+
+
+    @Override
+    public Canvas create(DataList dataList) {
+        DynamicForm form = new DynamicForm();
+        form.setWidth(200);
+        form.setHeight(35);
+
+        LinkItem next = new LinkItem();
+        next.setShowTitle(false);
+        next.setLinkTitle(MSG.next());
+
+        final ArtifactDescription desc = artifact.getArtifactDescription();
+        final String[] reachable       = desc.getReachableStates();
+
+        next.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent e) {
+                fireOnAdvance(reachable[0]);
+            }
+        });
+
+        form.setFields(next);
+
+        return form;
+    }
+
+
+    @Override
+    public Canvas createOld(DataList dataList) {
+        return null;
+    }
+
+
+    @Override
+    protected Data[] getData() {
+        return new Data[0];
+    }
+
+
+    public void addAdvanceHandler(AdvanceHandler handler) {
+        if (advHandlers == null) {
+            advHandlers = new ArrayList<AdvanceHandler>();
+        }
+
+        if (handler != null) {
+            advHandlers.add(handler);
+        }
+    }
+
+
+    public void fireOnAdvance(String target) {
+        if (advHandlers == null || advHandlers.isEmpty()) {
+            return;
+        }
+
+        for (AdvanceHandler handler: advHandlers) {
+            handler.onAdvance(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/ui/DatacageButton.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,49 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.Button;
+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.shared.model.User;
+
+
+public class DatacageButton extends Button implements ClickHandler {
+
+    protected Artifact artifact;
+    protected User     user;
+    protected CollectionView view;
+
+    public DatacageButton(
+        String         title, 
+        Artifact       artifact, 
+        User           user,
+        CollectionView view
+    ) {
+        super(title);
+
+        this.artifact = artifact;
+        this.user     = user;
+        this.view     = view;
+
+        addClickHandler(this);
+    }
+
+
+    public void onClick(ClickEvent event) {
+        // TODO: fetch outs!
+        String outs = "";
+
+        final DatacageWindow dc = new DatacageWindow(
+            artifact, user, outs, view);
+        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/DatacagePairWidget.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,106 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Button;
+
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.events.ClickEvent;
+
+import com.smartgwt.client.widgets.grid.ListGrid;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.User;
+import de.intevation.flys.client.shared.model.ToLoad;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * Widget showing two Datacages and a plus-button.
+ * Insert a record into a listgrid when plus-button clicked.
+ */
+public class DatacagePairWidget
+extends      VLayout
+{
+    protected FLYSConstants messages =
+        GWT.create(FLYSConstants.class);
+    
+    /** The "remote" ListGrid to insert data to when add-button is clicked. */
+    protected ListGrid grid; 
+    
+    /** First (upper) DataCage Grid. */
+    protected DatacageWidget firstDatacageWidget;
+    
+    /** Second (lower) DataCage Grid. */
+    protected DatacageWidget secondDatacageWidget;
+
+    
+    /**
+     *
+     * @param artifact Artifact to query datacage with.
+     * @param user     User to query datacage with.
+     * @param outs     outs to query datacage with.
+     * @param grid     Grid into which to insert selection of pairs.
+     */
+    public DatacagePairWidget(Artifact artifact,
+         User user,
+         String outs,
+         ListGrid grid) {
+        this.grid = grid;
+
+        HLayout hLayout      = new HLayout();
+        firstDatacageWidget  = new DatacageWidget(
+            artifact,
+            user,
+            outs, 
+            "load-system:true",
+            false);
+        secondDatacageWidget = new DatacageWidget(
+            artifact,
+            user,
+            outs,
+            "load-system:true",
+            false);
+        firstDatacageWidget.setIsMutliSelectable(false);
+        secondDatacageWidget.setIsMutliSelectable(false);
+
+        hLayout.addMember(firstDatacageWidget);
+        hLayout.addMember(secondDatacageWidget);
+
+        // TODO: i18n + icon
+        Button plusBtn = new Button("+");
+        plusBtn.addClickHandler(new ClickHandler() {
+            @Override
+            public void onClick(ClickEvent event) {
+                plusClicked();
+            }
+        });
+
+        addMember(hLayout);
+        addMember(plusBtn);
+    }
+
+
+    /**
+     * Callback for add-button.
+     * Fires to load for every selected element and handler.
+     */
+    public void plusClicked() {
+        ToLoad toLoad1 = firstDatacageWidget.getSelection();
+        ToLoad toLoad2 = secondDatacageWidget.getSelection();
+
+        // TODO further sanitize (toRecommendations.length)
+        if (toLoad1 == null || toLoad2 == null) {
+            return;
+        }
+
+        grid.addData(new RecommendationPairRecord(
+            toLoad1.toRecommendations().get(0),
+            toLoad2.toRecommendations().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/client/ui/DatacagePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,92 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.User;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class DatacagePanel extends TextProvider {
+
+    protected String dataName;
+
+    protected User user;
+
+    protected DatacageWidget widget;
+
+
+    public DatacagePanel() {
+        super();
+    }
+
+
+    public DatacagePanel(User user) {
+        super();
+        this.user = user;
+    }
+
+
+    @Override
+    public Canvas create(DataList dataList) {
+        Data   data   = dataList.get(0);
+        this.dataName = data.getLabel();
+
+        createWidget();
+
+        Canvas label   = new Label(data.getDescription());
+        Canvas submit  = getNextButton();
+        VLayout layout = new VLayout();
+        label.setHeight(25);
+
+        layout.addMember(label);
+        layout.addMember(submit);
+        layout.setMembersMargin(10);
+
+        return layout;
+    }
+
+
+    protected void createWidget() {
+        widget = new DatacageWidget(
+            artifact,
+            getUser(),
+            getOuts(),
+            getParameters(),
+            false);
+
+        widget.setHeight100();
+
+        helperContainer.addMember(widget);
+    }
+
+
+    @Override
+    public List<String> validate() {
+        return new ArrayList<String>();
+    }
+
+
+    public User getUser() {
+        return user;
+    }
+
+
+    public String getOuts() {
+        return null;
+    }
+
+
+    public String getParameters() {
+        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/DatacageTwinPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,495 @@
+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.data.Record;
+import com.smartgwt.client.types.ListGridFieldType;
+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.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
+import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
+
+import de.intevation.flys.client.shared.model.Artifact;
+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.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.User;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.StepForwardEvent;
+import de.intevation.flys.client.shared.model.Recommendation;
+import de.intevation.flys.client.shared.model.Recommendation.Facet;
+import de.intevation.flys.client.shared.model.Recommendation.Filter;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.services.LoadArtifactService;
+import de.intevation.flys.client.client.services.LoadArtifactServiceAsync;
+import de.intevation.flys.client.client.services.RemoveArtifactService;
+import de.intevation.flys.client.client.services.RemoveArtifactServiceAsync;
+
+// TODO Probably better to branch off AbstractUIProvider.
+// TODO Merge with other datacage-widget impls.
+/**
+ * Panel containing a Grid and a "next" button. The Grid is fed by a
+ * DatacagePairWidget which is put in the input-helper area.
+ */
+public class DatacageTwinPanel
+extends      TextProvider {
+
+    protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected String dataName;
+
+    protected User user;
+
+    /** ListGrid that displays user-selected pairs to build differences with. */
+    protected ListGrid differencesList;
+
+    /*
+     * List to track previously selected but now removed pairs. (Needed to
+     * be able to identify artifacts that can be removed from the collection.
+     */
+    protected List<RecommendationPairRecord> removedPairs =
+        new ArrayList<RecommendationPairRecord>();
+
+    /** Service handle to clone and add artifacts to collection. */
+    LoadArtifactServiceAsync loadArtifactService = GWT.create(
+            de.intevation.flys.client.client.services.LoadArtifactService.class);
+
+    /** Service to remove artifacts from collection. */
+    RemoveArtifactServiceAsync removeArtifactService = GWT.create(
+            de.intevation.flys.client.client.services.RemoveArtifactService.class);
+
+
+    public DatacageTwinPanel(User user) {
+        super();
+        this.user = user;
+    }
+
+
+    /**
+     * Remove first occurrence of "[" and "]" (if both do occur).
+     * @param value String to be stripped of [] (might be null).
+     * @return input string but with [ and ] removed, or input string if no
+     *         brackets were found.
+     * @see StringUtil.unbracket
+     */
+    public static final String unbracket(String value) {
+        // null- guard.
+        if (value == null) return value;
+
+        int start = value.indexOf("[");
+        int end   = value.indexOf("]");
+
+        if (start < 0 || end < 0) {
+            return value;
+        }
+
+        value = value.substring(start + 1, end);
+
+        return value;
+    }
+
+
+    /**
+     * Create a recommendation from a string representation of it.
+     * @TODO describe format of input string
+     * @param from string in format as shown above.
+     * @return recommendation from input string.
+     */
+    public Recommendation createRecommendationFromString(String from) {
+        // TODO Construct "real" filter.
+        String[] parts = unbracket(from).split(";");
+        Recommendation.Filter filter = new Recommendation.Filter();
+        Recommendation.Facet  facet  = new Recommendation.Facet(
+                parts[1],
+                parts[2]);
+
+        List<Recommendation.Facet> facets = new ArrayList<Recommendation.Facet>
+            ();
+        facets.add(facet);
+        filter.add("longitudinal_section", facets);
+        Recommendation r = new Recommendation("waterlevel", parts[0],
+            this.artifact.getUuid(), filter);
+        r.setDisplayName(parts[3]);
+        return r; 
+    }
+
+
+    /**
+     * Add RecomendationPairRecords from input String to the ListGrid.
+     */
+    public void populateGridFromString(String from){
+        // Split this string.
+        // Create according recommendations and display strings.
+        String[] recs = from.split("#");
+        if (recs.length % 2 != 0) return;
+        for (int i = 0; i < recs.length; i+=2) {
+            Recommendation minuend =
+                createRecommendationFromString(recs[i+0]);
+            Recommendation subtrahend =
+                createRecommendationFromString(recs[i+1]);
+
+            RecommendationPairRecord pr = new RecommendationPairRecord(
+                minuend, subtrahend);
+            // This Recommendation Pair comes from the data string and was thus
+            // already cloned.
+            pr.setIsAlreadyLoaded(true);
+            this.differencesList.addData(pr);
+        }
+    }
+
+
+    /**
+     * Creates the graphical representation and interaction widgets for the data.
+     * @param dataList the data.
+     * @return graphical representation and interaction widgets for data.
+     */
+    @Override
+    public Canvas create(DataList dataList) {
+        GWT.log("createData()");
+
+        Canvas widget = createWidget();
+        Canvas submit = getNextButton();
+
+        VLayout layout       = new VLayout();
+        HLayout helperLayout = new HLayout();
+        helperLayout.addMember(new DatacagePairWidget(this.artifact,
+            user, "waterlevels", differencesList));
+
+        layout.addMember(widget);
+        layout.addMember(submit);
+        layout.setMembersMargin(10);
+        this.helperContainer.addMember(helperLayout);
+
+        // Find old data, if any, handle "diffids".
+        Data   data   = dataList.get(0);
+        this.dataName = data.getLabel();
+        for (int i = 0; i < dataList.size(); i++) {
+            if (dataList.get(i) != null && dataList.get(i).getItems() != null) {
+                if (dataList.get(i).getItems() != null) {
+                    populateGridFromString(
+                        dataList.get(i).getItems()[0].getStringValue());
+                }
+            }
+        }
+
+        return layout;
+    }
+
+
+    /**
+     * Validates the selection.
+     * @return List of internationalized errror messages (if any).
+     */
+    @Override
+    public List<String> validate() {
+        List<String> errors = new ArrayList<String>();
+        if (differencesList.getRecords().length == 0) {
+            errors.add(MSG.error_no_waterlevel_pair_selected());
+        }
+        // Check whether minuend and subtrahend are equal.
+        for (ListGridRecord record: differencesList.getRecords()) {
+            RecommendationPairRecord r = (RecommendationPairRecord) record;
+            if (r.getFirst().equals(r.getSecond())) {
+                errors.add(MSG.error_same_waterlevels_in_pair());
+            }
+        }
+
+        return errors;
+    }
+
+
+    /**
+     * Creates layout with grid that displays selection inside.
+     */
+    public Canvas createWidget() {
+        VLayout layout  = new VLayout();
+        differencesList = new ListGrid();
+
+        differencesList.setCanEdit(false);
+        differencesList.setCanSort(false);
+        differencesList.setShowHeaderContextMenu(false);
+        differencesList.setHeight(150);
+        differencesList.setShowAllRecords(true);
+
+        ListGridField nameField    = new ListGridField("first",  "Minuend");
+        ListGridField capitalField = new ListGridField("second", "Subtrahend");
+        // Track removed rows, therefore more or less reimplement
+        // setCanRecomeRecords.
+        final ListGridField removeField  =
+            new ListGridField("_removeRecord", "Remove Record"){{
+                setType(ListGridFieldType.ICON);
+                setIcon(GWT.getHostPageBaseURL() + MSG.removeFeature());
+                setCanEdit(false);
+                setCanFilter(false);
+                setCanSort(false);
+                setCanGroupBy(false);
+                setCanFreeze(false);
+                setWidth(25);
+        }};
+
+        differencesList.setFields(new ListGridField[] {nameField,
+           capitalField, removeField});
+        
+        differencesList.addRecordClickHandler(new RecordClickHandler() {
+                public void onRecordClick(final RecordClickEvent event) {
+                    // Just handle remove-clicks
+                    if(!event.getField().getName().equals(removeField.getName())) {
+                        return;
+                    }    
+                    trackRemoved(event.getRecord());
+                    event.getViewer().removeData(event.getRecord());
+                }
+            });
+        layout.addMember(differencesList);
+
+        return layout;
+    }
+
+
+    /**
+     * Add record to list of removed records.
+     */
+    public void trackRemoved(Record r) {
+        RecommendationPairRecord pr = (RecommendationPairRecord) r;
+        this.removedPairs.add(pr);
+    }
+
+
+    /**
+     * Validates data, does nothing if invalid, otherwise clones new selected
+     * waterlevels and add them to collection, forward the artifact.
+     */
+    @Override
+    public void onClick(ClickEvent e) {
+        GWT.log("DatacageTwinPanel.onClick");
+
+        List<String> errors = validate();
+        if (errors != null && !errors.isEmpty()) {
+            showErrors(errors);
+            return;
+        }
+
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        ListGridRecord[] records = differencesList.getRecords();
+
+        List<Recommendation> ar  = new ArrayList<Recommendation>();
+        List<Recommendation> all = new ArrayList<Recommendation>();
+
+        for (ListGridRecord record : records) {
+            RecommendationPairRecord r =
+                (RecommendationPairRecord) record;
+            // Do not add "old" recommendations.
+            if (!r.isAlreadyLoaded()) {
+                // Check whether one of those is a dike or similar.
+                // TODO differentiate and merge: new clones, new, old.
+                Recommendation firstR = r.getFirst();
+                if(firstR.getIDs() != null) {
+                    GWT.log("First IDs: " + firstR.getIDs() + " factory: " + firstR.getFactory());
+                }
+                // TODO resolve this hack, fix datacage/recommendation for this case.
+                if(firstR.getIDs() != null &&
+                    firstR.getIDs().startsWith("flood_protection"))
+                {
+                    // These do not get cloned but loaded ("spawned").
+                    firstR.setFactory("staticwkms");
+                }
+                else {
+                    firstR.setFactory("waterlevel");
+                }
+                Recommendation secondR = r.getSecond();
+                if(secondR.getIDs() != null) {
+                    GWT.log("Second IDs: " + secondR.getIDs() + " factory: " + secondR.getFactory());
+                }
+                if(secondR.getIDs() != null
+                    && secondR.getIDs().startsWith("flood_protection"))
+                {
+                    // These do not get cloned but loaded ("spawned").
+                    secondR.setFactory("staticwkms");
+                }
+                else {
+                    secondR.setFactory("waterlevel");
+                }
+
+                ar.add(firstR);
+                ar.add(secondR);
+            }
+            else {
+                all.add(r.getFirst());
+                all.add(r.getSecond());
+            }
+        }
+
+        final Recommendation[] toClone = ar.toArray(new Recommendation[ar.size()]);
+        final Recommendation[] toUse   = all.toArray(new Recommendation[all.size()]);
+
+        // Find out whether "old" artifacts have to be removed.
+        List<String> artifactIdsToRemove = new ArrayList<String>();
+        for (RecommendationPairRecord rp: this.removedPairs) {
+            Recommendation first  = rp.getFirst();
+            Recommendation second = rp.getSecond();
+
+            for (Recommendation recommendation: toUse) {
+                if (first != null && first.getIDs().equals(recommendation.getIDs())) {
+                    first = null;
+                }
+                if (second != null && second.getIDs().equals(recommendation.getIDs())) {
+                    second = null;
+                }
+
+                if (first == null && second == null) {
+                    break;
+                }
+            }
+            if (first != null) {
+                artifactIdsToRemove.add(first.getIDs());
+            }
+            if (second != null) {
+                artifactIdsToRemove.add(second.getIDs());
+            }
+        }
+
+        // Remove old artifacts, if any. Do this asychronously without much
+        // feedback.
+        for(final String uuid: artifactIdsToRemove) {
+            removeArtifactService.remove(this.collection,
+                uuid,
+                url,
+                locale,
+                new AsyncCallback<Collection>() {
+                    public void onFailure(Throwable caught) {
+                        GWT.log("RemoveArtifact (" + uuid + ") failed.");
+                    }
+                    public void onSuccess(Collection collection) {
+                        GWT.log("RemoveArtifact succeeded");
+                    }
+                });
+        }
+
+        // Clone new ones (and spawn statics), go forward.
+        loadArtifactService.loadMany(
+            this.collection,
+            toClone,
+            //"staticwkms" and "waterlevel"
+            null,
+            url,
+            locale,
+            new AsyncCallback<Artifact[]>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Failure of cloning with factories!");
+                }
+                public void onSuccess(Artifact[] artifacts) {
+                    GWT.log("Successfully cloned " + toClone.length +
+                        " with factories.");
+
+                    fireStepForwardEvent(new StepForwardEvent(
+                        getData(toClone, artifacts, toUse)));
+                }
+            });
+    }
+
+
+    /**
+     * Create Data and DataItem from selection (a long string with identifiers
+     * to construct diff-pairs).
+     *
+     * @param newRecommendations "new" recommendations (did not survive a
+     *        backjump).
+     * @param newArtifacts artifacts cloned from newRecommendations.
+     * @param oldRecommendations old recommendations that survived a backjump.
+     *
+     * @return dataitem with a long string with identifiers to construct
+     *         diff-pairs.
+     */
+    protected Data[] getData(
+            Recommendation[] newRecommendations,
+            Artifact[] newArtifacts,
+            Recommendation[] oldRecommendations)
+    {
+        // Construct string with info about selections.
+        String dataItemString = "";
+        for (int i = 0; i < newRecommendations.length; i++) {
+            Recommendation r = newRecommendations[i];
+            Artifact newArtifact = newArtifacts[i];
+            String uuid = newArtifact.getUuid();
+            r.setMasterArtifact(uuid);
+            if (i>0) dataItemString += "#";
+    
+            dataItemString += createDataString(uuid, r);
+        }
+
+        for (int i = 0; i < oldRecommendations.length; i++) {
+            Recommendation r = oldRecommendations[i];
+            String uuid = r.getIDs();
+            if (dataItemString.length() > 0) dataItemString += "#";
+    
+            dataItemString += createDataString(uuid, r);
+        }
+
+        // TODO some hassle could be resolved by using multiple DataItems
+        // (e.g. one per pair).
+        DataItem item = new DefaultDataItem(dataName, dataName, dataItemString);
+        return new Data[] { new DefaultData(
+            dataName, null, null, new DataItem[] {item}) };
+    }
+
+
+    /**
+     * Creates part of the String that encodes minuend or subtrahend.
+     * @param artifact Artifacts UUID.
+     * @param recommendation Recommendation to wrap in string.
+     */
+    protected String createDataString(
+        String artifact,
+        Recommendation recommendation)
+    {
+        Filter filter = recommendation.getFilter();
+        Facet  f      = null;
+
+        if(filter != null) {
+            Map<String, List<Facet>>               outs = filter.getOuts();
+            Set<Map.Entry<String, List<Facet>>> entries = outs.entrySet();
+
+            for (Map.Entry<String, List<Facet>> entry: entries) {
+                List<Facet> fs = entry.getValue();
+
+                f = fs.get(0);
+                if (f != null) {
+                    break;
+                }
+            }
+
+            return "[" + artifact + ";"
+                + f.getName()
+                + ";"
+                + f.getIndex()
+                + ";"
+                + recommendation.getDisplayName() + "]";
+        }
+        else {
+            return "["
+                + artifact
+                + ";staticwkms;0;"
+                + recommendation.getDisplayName() + "]";
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/DatacageWidget.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,457 @@
+package de.intevation.flys.client.client.ui;
+
+import de.intevation.flys.client.client.event.DatacageHandler;
+import de.intevation.flys.client.client.event.DatacageDoubleClickHandler;
+
+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.Button;
+
+import com.smartgwt.client.widgets.tree.Tree;
+import com.smartgwt.client.widgets.tree.TreeGrid;
+import com.smartgwt.client.widgets.tree.TreeNode;
+
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.types.TreeModelType;
+
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.events.ClickEvent;
+
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+import com.smartgwt.client.widgets.grid.events.RecordDoubleClickEvent;
+import com.smartgwt.client.widgets.grid.events.RecordDoubleClickHandler;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.User;
+import de.intevation.flys.client.shared.model.ToLoad;
+
+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;
+import de.intevation.flys.client.shared.model.AttrList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.Stack;
+
+// TODO: refactor, extract ~DataCageGrid
+/**
+ * Display tree of, for example, previous calculations and allows
+ * selection in order to access/clone these.
+ */
+public class DatacageWidget
+extends      VLayout
+{
+    public static final int MAX_OPEN = 30;
+
+    protected MetaDataServiceAsync metaDataService =
+        GWT.create(MetaDataService.class);
+
+    protected FLYSConstants messages =
+        GWT.create(FLYSConstants.class);
+
+    protected Artifact artifact;
+    protected User     user;
+    protected String   outs;
+    protected String   parameters;
+
+    protected TreeGrid treeGrid;
+    protected Tree     tree;
+
+    protected ToLoad   toLoad;
+
+    protected List<DatacageHandler>            handlers;
+    protected List<DatacageDoubleClickHandler> doubleHandlers;
+
+
+    public DatacageWidget() {
+        handlers       = new ArrayList<DatacageHandler>();
+        doubleHandlers = new ArrayList<DatacageDoubleClickHandler>();
+    }
+
+
+    public DatacageWidget(Artifact artifact, User user) {
+        this(artifact, user, null);
+    }
+
+    public DatacageWidget(Artifact artifact, User user, String outs) {
+        this(artifact, user, outs, true);
+    }
+
+    public DatacageWidget(
+        Artifact   artifact,
+        User       user,
+        String     outs,
+        boolean    showButton
+    ) {
+        this(artifact, user, outs, null, showButton);
+    }
+
+
+    public DatacageWidget(
+        Artifact   artifact,
+        User       user,
+        String     outs,
+        String     parameters,
+        boolean    showButton
+    ) {
+        this();
+
+        this.artifact   = artifact;
+        this.user       = user;
+        this.outs       = outs;
+        this.parameters = parameters;
+
+        toLoad = new ToLoad();
+
+        setWidth100();
+
+        tree = new Tree();
+        tree.setModelType(TreeModelType.CHILDREN);
+        tree.setNameProperty("name");
+        tree.setIdField("id");
+        tree.setChildrenProperty("children-nodes");
+        tree.setShowRoot(false);
+
+        treeGrid = new TreeGrid();
+        treeGrid.setLoadDataOnDemand(false);
+        treeGrid.setWidth100();
+        treeGrid.setHeight100();
+        treeGrid.setShowRoot(false);
+        treeGrid.setNodeIcon("[SKIN]/images/blank.gif");
+        treeGrid.setShowConnectors(true);
+
+        treeGrid.addRecordDoubleClickHandler(new RecordDoubleClickHandler() {
+            @Override
+            public void onRecordDoubleClick(RecordDoubleClickEvent event) {
+                doubleClickedOnTree(event);
+            }
+        });
+
+        addMember(treeGrid);
+
+        if (showButton) {
+            addMember(createPlusButton());
+        }
+
+        triggerTreeBuilding();
+    }
+
+
+    /**
+     * @param handler Handler to be added (notified on add-action).
+     */
+    public DatacageWidget(Artifact artifact, User user, String outs,
+        DatacageHandler handler) {
+        this(artifact, user, outs);
+        this.addDatacageHandler(handler);
+    }
+
+
+    public DatacageWidget(
+        Artifact        artifact,
+        User            user,
+        String          outs,
+        DatacageHandler handler,
+        String          parameters
+    ) {
+        this(artifact, user, outs, handler);
+        this.parameters = parameters;
+    }
+
+
+    /**
+     * Sets whether more than one item can be selected.
+     * @param multi if true, allow mutliple selections.
+     */
+    public void setIsMutliSelectable(boolean multi) {
+        if (multi) {
+            treeGrid.setSelectionType(SelectionStyle.MULTIPLE);
+        }
+        else {
+            treeGrid.setSelectionType(SelectionStyle.SINGLE);
+        }
+    }
+
+
+    /**
+     * @param handler Handler to be added (notified on add-action).
+     */
+    public void addDatacageHandler(DatacageHandler handler) {
+        if (!handlers.contains(handler)) {
+            handlers.add(handler);
+        }
+    }
+
+
+    /**
+     * @param h Handler to be added (notified on Double click on node).
+     */
+    public void addDatacageDoubleClickHandler(DatacageDoubleClickHandler h) {
+        if (!doubleHandlers.contains(h)) {
+            doubleHandlers.add(h);
+        }
+    }
+
+
+    /**
+     * @param handler Handler to remove from list.
+     */
+    public void removeDatacageHandler(DatacageHandler handler) {
+        handlers.remove(handler);
+    }
+
+
+    public ToLoad getToLoad() {
+        return toLoad;
+    }
+
+
+    public ToLoad getSelection() {
+        // Reset content of toLoads.
+        toLoad = new ToLoad();
+
+        if (treeGrid == null) {
+            return toLoad;
+        }
+
+        ListGridRecord [] selection = treeGrid.getSelection();
+
+        if (selection != null) {
+            for (ListGridRecord record: selection) {
+                if (record instanceof TreeNode) {
+                    collectToLoads((TreeNode)record);
+                }
+            }
+        }
+
+        return toLoad;
+    }
+
+
+    /**
+     * Returns the titles of selected items (if any).
+     */
+    public String[] getSelectionTitles() {
+        if (treeGrid == null) {
+            return new String[] {};
+        }
+
+        ListGridRecord [] selection = treeGrid.getSelection();
+
+        if (selection == null) {
+            return new String[] {};
+        }
+
+        List<String> titleList = new ArrayList<String>();
+        for (ListGridRecord record: selection) {
+            if (record instanceof TreeNode) {
+                titleList.add(((TreeNode)record).getAttribute("name"));
+            }
+        }
+
+        return titleList.toArray(new String[titleList.size()]);
+    }
+
+
+    /**
+     * Callback for add-button.
+     * Fires to load for every selected element and handler.
+     */
+    public void plusClicked() {
+        if (!getSelection().isEmpty()) {
+            fireToLoad();
+        }
+    }
+
+
+    protected Button createPlusButton() {
+        Button plusBtn = new Button(messages.datacageAdd());
+        plusBtn.addClickHandler(new ClickHandler() {
+            @Override
+            public void onClick(ClickEvent event) {
+                plusClicked();
+            }
+        });
+        return plusBtn;
+    }
+
+
+    protected void fireToLoad() {
+        for (DatacageHandler handler: handlers) {
+            handler.toLoad(toLoad);
+        }
+    }
+
+
+    /** Notify DatacageDoubleClickHandlers that a doubleclick happened. */
+    protected void fireOnDoubleClick() {
+        for (DatacageDoubleClickHandler handler: doubleHandlers) {
+            handler.onDoubleClick(toLoad);
+        }
+    }
+
+
+    protected void doubleClickedOnTree(RecordDoubleClickEvent event) {
+        TreeNode node = (TreeNode)event.getRecord();
+        collectToLoads(node);
+        fireOnDoubleClick();
+    }
+
+
+    /**
+     * Adds to toLoad, from info in node.
+     * Afterwards, add all children of node to stack to parse (next time
+     * collectToLoads is called).
+     */
+    protected void collectToLoads(TreeNode node) {
+        Stack<TreeNode> stack = new Stack<TreeNode>();
+
+        stack.push(node);
+
+        while (!stack.isEmpty()) {
+            node = stack.pop();
+            String factory = node.getAttribute("factory");
+            if (factory != null) { // we need at least a factory
+                String artifact = node.getAttribute("artifact-id");
+                String out      = node.getAttribute("out");
+                String name     = node.getAttribute("facet");
+                String ids      = node.getAttribute("ids");
+                String debugAttributeValues = "";
+                for (String attr: node.getAttributes()) {
+                    debugAttributeValues += ("[" + attr +": "
+                        + node.getAttributeAsString(attr) + "] ");
+                }
+                GWT.log("DatacageWidget.collectToLoad, attributes are "
+                    + debugAttributeValues);
+
+                toLoad.add(artifact,
+                     factory,
+                     out,
+                     name,
+                     ids,
+                     node.getAttribute("name"));
+            }
+            TreeNode [] children = tree.getChildren(node);
+            if (children != null) {
+                for (TreeNode child: children) {
+                    stack.push(child);
+                }
+            }
+        }
+    }
+
+
+    protected void triggerTreeBuilding() {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        String artifactId = artifact.getUuid();
+        String userId     = (user != null) ? user.identifier() : null;
+
+        metaDataService.getMetaData(
+            url, locale,
+            artifactId,
+            userId,
+            outs,
+            parameters,
+            new AsyncCallback<DataCageTree>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not load meta data.");
+                    SC.warn(caught.getMessage());
+                }
+
+                public void onSuccess(DataCageTree dcTree) {
+                    GWT.log("Successfully loaded meta data.");
+                    IdGenerator idGenerator = new IdGenerator();
+                    DataCageNode dcRoot = dcTree.getRoot();
+                    TreeNode root = buildRecursiveChildren(
+                        dcRoot, idGenerator);
+                    tree.setRoot(root);
+                    if (idGenerator.current() < MAX_OPEN) {
+                        tree.openAll();
+                    }
+                    treeGrid.setData(tree);
+                }
+            });
+    }
+
+    private static final class IdGenerator {
+        protected int current;
+
+        public IdGenerator() {
+        }
+
+        public int next() {
+            return current++;
+        }
+
+        public int current() {
+            return current;
+        }
+    } // class IdGenerator
+
+    private String i18n(String s) {
+        if (!(s.startsWith("${") && s.endsWith("}"))) {
+            return s;
+        }
+
+        s = s.substring(2, s.length()-1);
+
+        try {
+            return messages.getString(s);
+        }
+        catch (MissingResourceException mre) {
+            GWT.log("cannot find i18n for + '" + s + "'");
+            return s;
+        }
+    }
+
+    protected TreeNode buildRecursiveChildren(
+        DataCageNode   node, 
+        IdGenerator    idGenerator
+    ) {
+        TreeNode tn = new TreeNode();
+        tn.setAttribute("id", idGenerator.next());
+
+        List<DataCageNode> children = node.getChildren();
+
+        if (children != null) {
+            TreeNode [] tns = new TreeNode[children.size()];
+            for (int i = 0; i < tns.length; ++i) {
+                DataCageNode child = children.get(i);
+                tns[i] = buildRecursiveChildren(child, idGenerator);
+            }
+            tn.setAttribute("children-nodes", tns);
+        }
+
+        tn.setAttribute("name", i18n(node.getDescription()));
+        tn.setAttribute("facet", node.getName());
+
+        AttrList attrs = node.getAttributes();
+        if (attrs != null) {
+            for (int i = 0, N = attrs.size(); i < N; ++i) {
+                String key   = attrs.getKey(i);
+                String value = attrs.getValue(i);
+                tn.setAttribute(key, value);
+            }
+        }
+
+        return tn;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,199 @@
+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.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Window;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYSConstants;
+
+import de.intevation.flys.client.client.event.DatacageHandler;
+import de.intevation.flys.client.client.event.DatacageDoubleClickHandler;
+import de.intevation.flys.client.client.event.HasRedrawRequestHandlers;
+import de.intevation.flys.client.client.event.RedrawRequestHandler;
+import de.intevation.flys.client.client.event.RedrawRequestEvent;
+import de.intevation.flys.client.client.event.RedrawRequestEvent.Type;
+
+import de.intevation.flys.client.client.services.LoadArtifactService;
+import de.intevation.flys.client.client.services.LoadArtifactServiceAsync;
+
+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.ToLoad;
+import de.intevation.flys.client.shared.model.User;
+import de.intevation.flys.client.shared.model.Recommendation;
+
+
+public class DatacageWindow
+extends      Window
+implements   DatacageHandler,
+             DatacageDoubleClickHandler,
+             HasRedrawRequestHandlers
+{
+    protected FLYSConstants messages =
+        GWT.create(FLYSConstants.class);
+
+    protected LoadArtifactServiceAsync loadService =
+        GWT.create(LoadArtifactService.class);
+
+    protected CollectionView view;
+
+    protected List<RedrawRequestHandler> handlers;
+
+    protected int inProgress;
+
+
+    public DatacageWindow(
+        Artifact       artifact, 
+        User           user,
+        String         outs,
+        CollectionView view
+    ) {
+        this.view       = view;
+        this.handlers   = new ArrayList<RedrawRequestHandler>();
+        this.inProgress = 0;
+
+        setWidth(400);
+        setHeight(500);
+
+        DatacageWidget dw = new DatacageWidget(
+            artifact,
+            user,
+            outs,
+            "load-system:true",
+            true);
+        dw.addDatacageHandler(this);
+        dw.addDatacageDoubleClickHandler(this);
+
+        addItem(dw);
+
+        String river =  findRiver(artifact);
+        // TODO: i18n
+        setTitle("Datenkorb: " + river);
+        setShowMinimizeButton(false);
+        setIsModal(true);
+        setShowModalMask(true);
+        setCanDragResize(true);
+
+        centerInPage();
+    }
+
+
+    @Override
+    public void toLoad(ToLoad toLoad) {
+        destroy();
+        List<Recommendation> recs = toLoad.toRecommendations();
+        loadArtifacts(recs.toArray(new Recommendation[recs.size()]));
+    }
+
+
+    @Override
+    public void onDoubleClick(ToLoad toLoad) {
+        destroy();
+        List<Recommendation> recs = toLoad.toRecommendations();
+        loadArtifacts(recs.toArray(new Recommendation[recs.size()]));
+    }
+
+
+    @Override
+    public void addRedrawRequestHandler(RedrawRequestHandler handler) {
+        if (handler != null) {
+            handlers.add(handler);
+        }
+    }
+
+
+    protected String findRiver(Artifact artifact) {
+        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 "";
+    }
+
+
+    protected void decreateInProgress() {
+        if (this.inProgress > 0) {
+            this.inProgress--;
+        }
+
+        if (this.inProgress == 0) {
+            fireRedrawRequest();
+        }
+    }
+
+
+    protected void fireRedrawRequest() {
+        RedrawRequestEvent evt = new RedrawRequestEvent(Type.DEFAULT);
+
+        for (RedrawRequestHandler handler: handlers) {
+            handler.onRedrawRequest(evt);
+        }
+    }
+
+
+    protected void loadArtifacts(Recommendation[] recommendations) {
+        Config cfg = Config.getInstance();
+
+        final Collection collection     = view.getCollection();
+        final Artifact   masterArtifact = view.getArtifact();
+        final String     serverUrl      = cfg.getServerUrl();
+        final String     locale         = cfg.getLocale();
+
+        this.inProgress = recommendations.length;
+
+        for (final Recommendation recommendation: recommendations) {
+            // XXX: UGLY! If no reference artifact given use uuid of
+            //      current artifact as reference.
+            if (recommendation.getMasterArtifact() == null) {
+                recommendation.setMasterArtifact(masterArtifact.getUuid());
+            }
+
+            final String factory = recommendation.getFactory();
+
+            GWT.log("Load new artifact with factory: " + factory);
+
+            loadService.load(
+                collection,
+                recommendation,
+                factory,
+                serverUrl,
+                locale,
+                new AsyncCallback<Artifact>() {
+                    public void onFailure(Throwable caught) {
+                        decreateInProgress();
+                        SC.warn(caught.getMessage());
+                    }
+
+                    public void onSuccess(Artifact artifact) {
+                        decreateInProgress();
+                        GWT.log("Created new artifact: " + artifact.getUuid());
+                    }
+            });
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/DemDatacagePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,72 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.Recommendation;
+import de.intevation.flys.client.shared.model.ToLoad;
+import de.intevation.flys.client.shared.model.User;
+
+
+public class DemDatacagePanel extends DatacagePanel {
+
+    public static final String OUT        = "floodmap";
+    public static final String PARAMETERS = "dem:true";
+
+
+    public DemDatacagePanel() {
+        super();
+    }
+
+
+    public DemDatacagePanel(User user) {
+        super(user);
+    }
+
+
+    @Override
+    protected void createWidget() {
+        super.createWidget();
+        widget.setIsMutliSelectable(false);
+    }
+
+
+    @Override
+    public User getUser() {
+        return null;
+    }
+
+
+    @Override
+    public String getOuts() {
+        return OUT;
+    }
+
+
+    @Override
+    public String getParameters() {
+        return PARAMETERS;
+    }
+
+
+    @Override
+    protected Data[] getData() {
+        Recommendation r = getSelectedRecommendation();
+
+        DataItem item = new DefaultDataItem(dataName, dataName, r.getIDs());
+        return new Data[] { new DefaultData(
+            dataName, null, null, new DataItem[] { item }) };
+    }
+
+
+    protected Recommendation getSelectedRecommendation() {
+        ToLoad toLoad = widget.getSelection();
+        List<Recommendation> recoms = toLoad.toRecommendations();
+
+        return recoms.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/client/ui/DigitizePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,192 @@
+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.events.ResizedEvent;
+import com.smartgwt.client.widgets.events.ResizedHandler;
+import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
+import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
+
+import org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
+import org.gwtopenmaps.openlayers.client.format.GeoJSON;
+import org.gwtopenmaps.openlayers.client.layer.WMS;
+import org.gwtopenmaps.openlayers.client.layer.WMSParams;
+import org.gwtopenmaps.openlayers.client.layer.WMSOptions;
+
+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.MapInfo;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.ui.map.FloodMap;
+import de.intevation.flys.client.client.ui.map.MapPanel;
+import de.intevation.flys.client.client.services.MapInfoService;
+import de.intevation.flys.client.client.services.MapInfoServiceAsync;
+
+
+public class DigitizePanel
+extends SelectProvider
+implements TabSelectedHandler {
+
+    protected MapInfoServiceAsync mapInfo = GWT.create(MapInfoService.class);
+
+    protected FloodMap floodMap;
+
+
+    public static final String UESK_BARRIERS = "uesk.barriers";
+
+
+    public DigitizePanel() {
+    }
+
+
+    @Override
+    public Canvas create(DataList list) {
+        List<Data> data = list.getAll();
+
+        Data barriers = null;
+        for (int i = data.size()-1; i >= 0; i--) {
+            Data d = data.get(i);
+            if (d.getLabel().equals(UESK_BARRIERS)) {
+                barriers = d;
+                data.remove(d);
+            }
+        }
+
+        DataList clone = (DataList) list.clone();
+        List<Data> all = clone.getAll();
+        all.remove(UESK_BARRIERS);
+
+        Canvas selectBox = super.create(clone);
+
+        final Config cfg    = Config.getInstance();
+        final String url    = cfg.getServerUrl();
+        final String locale = cfg.getLocale();
+
+        DataItem[] obj = barriers.getItems();
+
+        final String[] geojson = new String[1];
+        for (DataItem item: obj) {
+            if (item.getLabel().equals(UESK_BARRIERS)) {
+                geojson[0] = item.getStringValue();
+                break;
+            }
+        }
+
+        String river = getDataValue("state.winfo.river", "river");
+        mapInfo.getMapInfo(url, locale, river, new AsyncCallback<MapInfo>() {
+            public void onFailure(Throwable caught) {
+                String msg = caught.getMessage();
+
+                GWT.log("Error while fetching MapInfo: " + msg);
+                SC.warn(MSG.getString(msg));
+            }
+
+            public void onSuccess(MapInfo info) {
+                createMapWidget(info, geojson[0]);
+            }
+        });
+
+        return selectBox;
+    }
+
+
+    @Override
+    protected Data[] getData() {
+        Data[] data  = super.getData();
+        Data[] total = new Data[2];
+
+        DataItem item = new DefaultDataItem(
+            UESK_BARRIERS, UESK_BARRIERS, floodMap.getFeaturesAsGeoJSON());
+
+        total[0] = data[0];
+        total[1] = new DefaultData(
+            UESK_BARRIERS, null, null, new DataItem[] { item });
+
+        return total;
+    }
+
+
+    public void createMapWidget(MapInfo mapInfo, String geojson) {
+        final MapPanel mapPanel = new MapPanel(mapInfo, true);
+
+        floodMap = mapPanel.getFloodMap();
+        Map map  = floodMap.getMap();
+
+        helperContainer.addMember(mapPanel);
+        helperContainer.addResizedHandler(new ResizedHandler() {
+            public void onResized(ResizedEvent e) {
+                Integer height = helperContainer.getHeight();
+                Integer width  = helperContainer.getWidth();
+
+                height = height * 99 / 100;
+                width  = width  * 99 / 100;
+
+                String w = String.valueOf(width) + "px";
+                String h = String.valueOf(height) + "px";
+
+                mapPanel.getFloodMap().setSize(w, h);
+            }
+        });
+
+        parameterList.registerCollectionViewTabHandler(this);
+
+        WMS axis = getLayer(
+            mapInfo.getWmsUrl(), "riveraxis",
+            mapInfo.getProjection(), false);
+        WMS back = getLayer(
+            mapInfo.getBackgroundWmsUrl(), mapInfo.getBackgroundWmsLayers(),
+            mapInfo.getProjection(), false);
+
+        map.addLayer(axis);
+        map.addLayer(back);
+
+        if (geojson != null && geojson.length() > 0) {
+            VectorFeature[] features = new GeoJSON().read(geojson);
+            floodMap.getBarrierLayer().addFeatures(features);
+        }
+
+        map.zoomToMaxExtent();
+    }
+
+
+    protected WMS getLayer(String url, String layers, String proj, boolean x) {
+        WMSParams params = new WMSParams();
+        params.setLayers(layers);
+        params.setFormat("image/png");
+        params.setIsTransparent(!x);
+
+        WMSOptions opts = new WMSOptions();
+        opts.setTransitionEffect("null");
+        opts.setProjection(proj);
+        opts.setSingleTile(true);
+        opts.setRatio(1);
+        opts.setBuffer(0);
+
+        WMS wms = new WMS(layers, url, params, opts);
+        wms.setIsVisible(true);
+        wms.setIsBaseLayer(x);
+
+        return wms;
+    }
+
+
+    public void onTabSelected(TabSelectedEvent tse) {
+        if (tse.getTabNum () != 0) {
+            floodMap.hideBarrierLayer();
+        }
+        else {
+            floodMap.showBarrierLayer();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/DistanceOnlyPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,71 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+
+
+public class DistanceOnlyPanel extends DistancePanel {
+
+    public DistanceOnlyPanel() {
+        this("right");
+    }
+
+
+    public DistanceOnlyPanel(String labelOrientation) {
+        distancePanel = new DoubleRangeOnlyPanel(
+            labelFrom(), labelTo(), 0d, 0d, 250, this, labelOrientation);
+    }
+
+
+    @Override
+    protected String getOldSelectionString(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data dFrom = getData(items, getLowerField());
+        Data dTo   = getData(items, getUpperField());
+
+        DataItem[] from = dFrom.getItems();
+        DataItem[] to   = dTo.getItems();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(from[0].getLabel());
+        sb.append(" " + getUnitFrom() + " - ");
+        sb.append(to[0].getLabel());
+        sb.append(" " + getUnitTo());
+
+        return sb.toString();
+    }
+
+
+    @Override
+    protected void initDefaultStep(DataList data) {
+        // do nothing
+    }
+
+
+    @Override
+    public Data[] getData() {
+        Data[] data = new Data[2];
+
+        data[0] = getDataFrom();
+        data[1] = getDataTo();
+
+        return data;
+    }
+
+
+    @Override
+    protected String labelFrom() {
+        return getUnitFrom() + " - ";
+    }
+
+
+    @Override
+    protected String labelTo() {
+        return getUnitTo();
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/DistancePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,530 @@
+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.smartgwt.client.data.Criteria;
+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.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
+import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
+import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.TabSet;
+import com.smartgwt.client.widgets.tab.Tab;
+import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
+import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
+
+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.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.Config;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.ui.range.DistanceInfoDataSource;
+import de.intevation.flys.client.client.ui.range.RangeTable;
+import de.intevation.flys.client.client.ui.range.LocationsTable;
+import de.intevation.flys.client.client.event.FilterHandler;
+import de.intevation.flys.client.client.event.StringFilterEvent;
+
+
+public class DistancePanel extends AbstractUIProvider implements BlurHandler, FilterHandler
+{
+
+    public static final int DEFAULT_STEP_WIDTH = 100;
+
+    public static final String FIELD_LOWER = "ld_from";
+    public static final String FIELD_UPPER = "ld_to";
+    public static final String FIELD_STEP  = "ld_step";
+
+
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected RangeTable     distancesTable;
+    protected LocationsTable locationsTable;
+
+    protected DoubleRangePanel distancePanel;
+
+    protected TableFilter filter;
+
+    protected TabSet tabs;
+
+    protected double min;
+    protected double max;
+
+
+    public DistancePanel() {
+        this("right");
+    }
+
+
+    public DistancePanel(String labelOrientation) {
+        distancePanel  = new DoubleRangePanel(
+            labelFrom(), labelTo(), labelStep(),
+            0d, 0d, 0d, 250, this, labelOrientation);
+    }
+
+
+    @Override
+    public Canvas create(DataList data) {
+        Data d = data.get(0);
+
+        VLayout layout = new VLayout();
+        layout.setMembersMargin(10);
+
+        Label label = new Label(MSG.distance_state());
+
+        Canvas submit = getNextButton();
+
+        label.setHeight(25);
+        distancePanel.setHeight(50);
+
+        layout.addMember(label);
+        layout.addMember(distancePanel);
+        layout.addMember(submit);
+
+        initMinMaxValues(data);
+        initDefaultValues(data);
+        initHelperPanel();
+
+        return layout;
+    }
+
+
+    @Override
+    public Canvas createOld(DataList dataList) {
+        String s = getOldSelectionString(dataList);
+        String l = dataList.getLabel();
+
+        Label label    = new Label(l);
+        Label selected = new Label(s);
+
+        HLayout layout = new HLayout();
+
+        layout.setWidth(400);
+        label.setWidth(200);
+        selected.setWidth(130);
+
+        layout.addMember(label);
+        layout.addMember(selected);
+        layout.addMember(getBackButton(dataList.getState()));
+
+        return layout;
+    }
+
+
+    protected String getOldSelectionString(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data dFrom = getData(items, getLowerField());
+        Data dTo   = getData(items, getUpperField());
+        Data dStep = getData(items, getStepField());
+
+        DataItem[] from = dFrom.getItems();
+        DataItem[] to   = dTo.getItems();
+        DataItem[] step = dStep.getItems();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(from[0].getLabel());
+        sb.append(" " + getUnitFrom() + " - ");
+        sb.append(to[0].getLabel());
+        sb.append(" " + getUnitTo() + " - ");
+        sb.append(step[0].getLabel());
+        sb.append(" " + getUnitStep());
+
+        return sb.toString();
+    }
+
+
+    protected String labelFrom() {
+        return getLabelFrom() + " [" + getUnitFrom() + "]";
+    }
+
+
+    protected String getLabelFrom() {
+        return MSG.dpLabelFrom();
+    }
+
+
+    protected String getUnitFrom() {
+        return MSG.dpUnitFrom();
+    }
+
+
+    protected String labelTo() {
+        return getLabelTo() + " [" + getUnitTo() + "]";
+    }
+
+
+    protected String getLabelTo() {
+        return MSG.dpLabelTo();
+    }
+
+
+    protected String getUnitTo() {
+        return MSG.dpUnitTo();
+    }
+
+
+    protected String labelStep() {
+        return getLabelStep() + " [" + getUnitStep() + "]";
+    }
+
+
+    protected String getLabelStep() {
+        return MSG.dpLabelStep();
+    }
+
+
+    protected String getUnitStep() {
+        return MSG.dpUnitStep();
+    }
+
+
+    protected String getLowerField() {
+        return FIELD_LOWER;
+    }
+
+
+    protected String getUpperField() {
+        return FIELD_UPPER;
+    }
+
+
+    protected String getStepField() {
+        return FIELD_STEP;
+    }
+
+
+    @Override
+    public List<String> validate() {
+        List<String> errors = new ArrayList<String>();
+
+        if (!distancePanel.validateForm()) {
+            errors.add(MSG.wrongFormat());
+
+            return errors;
+        }
+
+        validateFrom(errors);
+        validateTo(errors);
+
+        return errors;
+    }
+
+
+    protected void validateFrom(List<String> errors) {
+        double from = distancePanel.getFrom();
+
+        if (from < min || from > max) {
+            NumberFormat nf = NumberFormat.getDecimalFormat();
+
+            String tmp = MSG.error_validate_lower_range();
+            tmp        = tmp.replace("$1", nf.format(from));
+            tmp        = tmp.replace("$2", nf.format(min));
+
+            distancePanel.setFrom(min);
+            errors.add(tmp);
+        }
+    }
+
+
+    protected void validateTo(List<String> errors) {
+        double to = distancePanel.getTo();
+
+        if (to < min || to > max) {
+            NumberFormat nf = NumberFormat.getDecimalFormat();
+
+            String tmp = MSG.error_validate_upper_range();
+            tmp        = tmp.replace("$1", nf.format(to));
+            tmp        = tmp.replace("$2", nf.format(max));
+
+            distancePanel.setTo(max);
+            errors.add(tmp);
+        }
+    }
+
+
+    @Override
+    public Data[] getData() {
+        Data[] data = new Data[4];
+
+        data[0] = getDataFrom();
+        data[1] = getDataTo();
+        data[2] = getDataStep();
+
+        DataItem item = new DefaultDataItem("ld_mode","ld_mode", "distance");
+        data[3]       = new DefaultData(
+            "ld_mode", null, null, new DataItem[] { item });
+
+        return data;
+    }
+
+
+    protected Data getDataFrom() {
+        String value = String.valueOf(distancePanel.getFrom());
+        String field = getLowerField();
+
+        DataItem item = new DefaultDataItem(field, field, value);
+        return new DefaultData(
+            field, null, null, new DataItem[] { item });
+    }
+
+
+    protected Data getDataTo() {
+        String value = String.valueOf(distancePanel.getTo());
+        String field = getUpperField();
+
+        DataItem item = new DefaultDataItem(field, field, value);
+        return new DefaultData(
+            field, null, null, new DataItem[] { item });
+    }
+
+
+    protected Data getDataStep() {
+        String value = String.valueOf(distancePanel.getStep());
+        String field = getStepField();
+
+        DataItem item = new DefaultDataItem(field, field, value);
+        return new DefaultData(
+            field, null, null, new DataItem[] { item });
+    }
+
+
+    @Override
+    public void onBlur(BlurEvent event) {
+        distancePanel.validateForm();
+    }
+
+
+    protected void initMinMaxValues(DataList data) {
+        Data f = getData(data.getAll(), getLowerField());
+        Data t = getData(data.getAll(), getUpperField());
+
+        DataItem[] fItems = f.getItems();
+        DataItem[] tItems = t.getItems();
+
+        try {
+            min = Double.valueOf(fItems[0].getStringValue());
+            max = Double.valueOf(tItems[0].getStringValue());
+        }
+        catch (NumberFormatException nfe) {
+            min = -Double.MAX_VALUE;
+            max =  Double.MAX_VALUE;
+        }
+    }
+
+
+    protected void initDefaultValues(DataList data) {
+        initDefaultFrom(data);
+        initDefaultTo(data);
+        initDefaultStep(data);
+    }
+
+
+    protected void initDefaultFrom(DataList data) {
+        Data f = getData(data.getAll(), getLowerField());
+
+        double from = getDefaultFrom();
+
+        try {
+            from = getDefaultValue(f);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        distancePanel.setFrom(from);
+    }
+
+
+    protected double getDefaultFrom() {
+        return min;
+    }
+
+
+    protected void initDefaultTo(DataList data) {
+        Data t = getData(data.getAll(), getUpperField());
+
+        double to = getDefaultTo();
+
+        try {
+            to = getDefaultValue(t);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        distancePanel.setTo(to);
+    }
+
+
+    protected double getDefaultTo() {
+        return max;
+    }
+
+
+    protected void initDefaultStep(DataList data) {
+        Data s = getData(data.getAll(), getStepField());
+
+        double step = getDefaultStep();
+
+        try {
+            step = getDefaultValue(s);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        distancePanel.setStep(step);
+    }
+
+
+    protected double getDefaultStep() {
+        return (double) DEFAULT_STEP_WIDTH;
+    }
+
+
+    protected double getDefaultValue(Data data)
+    throws NumberFormatException
+    {
+        DataItem def      = data.getDefault();
+        String   defValue = def != null ? def.getStringValue() : null;
+
+        return Double.valueOf(defValue);
+    }
+
+
+    protected void initHelperPanel() {
+        distancesTable = new RangeTable();
+        locationsTable = new LocationsTable();
+
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String river  = getRiverName();
+
+        distancesTable.setAutoFetchData(true);
+        locationsTable.setAutoFetchData(true);
+        distancesTable.setDataSource(new DistanceInfoDataSource(
+            url, river, "distances"));
+        locationsTable.setDataSource(new DistanceInfoDataSource(
+            url, river, "locations"));
+
+        distancesTable.addRecordClickHandler(new RecordClickHandler() {
+            public void onRecordClick(RecordClickEvent e) {
+                Record r = e.getRecord();
+
+                String from = r.getAttribute("from");
+                String to   = r.getAttribute("to");
+
+                try {
+                    distancePanel.setFrom(Double.valueOf(from));
+                    distancePanel.setTo(Double.valueOf(to));
+                }
+                catch (NumberFormatException nfe) {
+                    SC.warn(MSG.wrongFormat());
+                }
+            }
+        });
+
+        locationsTable.addRecordClickHandler(new RecordClickHandler() {
+            public void onRecordClick(RecordClickEvent e) {
+                Record  r = e.getRecord();
+                int field = e.getFieldNum();
+
+                try {
+                    String value = r.getAttribute("from");
+
+                    switch (field) {
+                    case 0:
+                        distancePanel.setFrom(Double.valueOf(value));
+                        break;
+                    case 1:
+                        distancePanel.setTo(Double.valueOf(value));
+                        break;
+                    }
+                }
+                catch (NumberFormatException nfe) {
+                    SC.warn(MSG.wrongFormat());
+                }
+            }
+        });
+
+        tabs = new TabSet();
+        tabs.setWidth100();
+        tabs.setHeight100();
+
+        Tab locations = new Tab(MSG.locations());
+        Tab distances = new Tab(MSG.distance());
+
+        locations.setPane(locationsTable);
+        distances.setPane(distancesTable);
+
+        tabs.addTab(locations, 0);
+        tabs.addTab(distances, 1);
+
+        filter = new TableFilter();
+        filter.setHeight("30px");
+        filter.addFilterHandler(this);
+
+        tabs.addTabSelectedHandler(new TabSelectedHandler() {
+            public void onTabSelected(TabSelectedEvent evt) {
+                filter.clear();
+            }
+        });
+
+        helperContainer.addMember(tabs);
+        helperContainer.addMember(filter);
+    }
+
+
+    public void onFilterCriteriaChanged(StringFilterEvent event) {
+        String search = event.getFilter();
+
+        if (search != null && search.length() > 0) {
+            Criteria c = new Criteria("description", search);
+            locationsTable.filterData(c);
+            distancesTable.filterData(c);
+        }
+        else {
+            locationsTable.clearCriteria();
+            distancesTable.clearCriteria();
+        }
+    }
+
+
+    protected String getRiverName() {
+        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) {
+                           return d.getItems()[0].getStringValue();
+                        }
+                    }
+                }
+            }
+        }
+
+        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/DoubleArrayPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,265 @@
+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();
+
+        if(values.length == 0) {
+            ti.clearValue();
+            return;
+        }
+        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/DoubleRangeOnlyPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,88 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.widgets.form.fields.FloatItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+
+
+public class DoubleRangeOnlyPanel extends DoubleRangePanel {
+
+    public DoubleRangeOnlyPanel(
+        String      titleFrom,
+        String      titleTo,
+        double      from,
+        double      to,
+        int         width,
+        BlurHandler handler
+    ) {
+        this(titleFrom, titleTo, from, to, width, handler, "right");
+    }
+
+
+    public DoubleRangeOnlyPanel(
+        String      titleFrom,
+        String      titleTo,
+        double      from,
+        double      to,
+        int         width,
+        BlurHandler handler,
+        String      labelOrientation
+    ) {
+        super();
+
+        fromItem = new FloatItem(FIELD_FROM);
+        toItem   = new FloatItem(FIELD_TO);
+        stepItem = new FloatItem(FIELD_WIDTH);
+
+        fromItem.addBlurHandler(handler);
+        toItem.addBlurHandler(handler);
+
+        NumberFormat nf = NumberFormat.getDecimalFormat();
+
+        fromItem.setValue(nf.format(from));
+        toItem.setValue(nf.format(to));
+
+        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);
+
+        int itemWidth = width / 4;
+        fromItem.setWidth(itemWidth);
+        fromText.setWidth(itemWidth);
+        toItem.setWidth(itemWidth);
+        toText.setWidth(itemWidth);
+
+        if (labelOrientation.equals("right")) {
+            setFields(fromItem, fromText, toItem, toText);
+        }
+        else {
+            setFields(fromText, fromItem, toText, toItem);
+        }
+
+        setFixedColWidths(false);
+        setNumCols(4);
+        setWidth(width);
+        setAlign(Alignment.CENTER);
+    }
+
+
+    @Override
+    public boolean validateForm() {
+        return validateForm(fromItem) && validateForm(toItem);
+    }
+
+
+    @Override
+    public double getStep() {
+        return -1;
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,290 @@
+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;
+
+
+    public DoubleRangePanel() {
+    }
+
+
+    /**
+     * 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)
+    {
+        this(
+            titleFrom, titleTo, titleStep,
+            from, to, step,
+            width,
+            handler,
+            "right");
+    }
+
+
+    public DoubleRangePanel(
+        String titleFrom, String titleTo, String titleStep,
+        double from, double to, double step,
+        int width,
+        BlurHandler handler,
+        String labelOrientation)
+    {
+        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);
+
+        if (labelOrientation.equals("right")) {
+            setFields(fromItem, fromText, toItem, toText, stepItem, stepText);
+        }
+        else {
+            setFields(fromText, fromItem, toText, toItem, stepText, stepItem);
+        }
+
+        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);
+    }
+
+
+    /**
+     * Sets a new step width.
+     *
+     * @param value The new step width.
+     */
+    public void setStep(double value) {
+        setDoubleValue(value, FIELD_WIDTH);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,133 @@
+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.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();
+
+        ImgLink link = new ImgLink(iUrl, url, 30, 30);
+        link.setTooltip(getTooltipText(name, facet));
+
+        return link;
+    }
+
+
+    /**
+     * 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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,94 @@
+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();
+        }
+    }
+
+
+    public void updateTitle(String uuid, String title) {
+        CollectionView view = views.get(uuid);
+        view.setTitle(title);
+    }
+
+
+    public boolean hasView(String uuid) {
+        if(views.get(uuid) != null) {
+            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-client/src/main/java/de/intevation/flys/client/client/ui/ImgLink.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.types.Overflow;
+import com.smartgwt.client.widgets.HTMLPane;
+
+
+public class ImgLink extends HTMLPane {
+
+    public ImgLink(String imgUrl, String href, int width, int height) {
+        super();
+
+        setContents("<a href='" + href + "'><img src='" + imgUrl + "'></a>");
+        setWidth(width);
+        setHeight(height);
+        setOverflow(Overflow.VISIBLE);
+    }
+}
--- /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:51 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/LoadingPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,185 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ImageResource;
+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.Positioning;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.services.DescribeArtifactService;
+import de.intevation.flys.client.client.services.DescribeArtifactServiceAsync;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.CalculationMessage;
+
+
+public class LoadingPanel extends Canvas {
+
+    public static final int UPDATE_INTERVAL = 1000 * 3;
+
+    public static final DescribeArtifactServiceAsync describe =
+        GWT.create(DescribeArtifactService.class);
+
+    private FLYSImages    IMAGES = GWT.create(FLYSImages.class);
+    private FLYSConstants MSG    = GWT.create(FLYSConstants.class);
+
+
+    protected CollectionView parent;
+    protected Artifact       artifact;
+
+    protected VLayout dialog;
+    protected Label   msg;
+    protected Label   title;
+
+    protected int i;
+
+
+    public LoadingPanel(CollectionView parent, Artifact artifact) {
+        super();
+
+        this.parent   = parent;
+        this.artifact = artifact;
+        this.msg      = new Label("");
+        this.title    = new Label("");
+        this.dialog   = createDialog();
+
+        this.i = 0;
+
+        initLayout();
+        startTimer();
+    }
+
+
+    private void initLayout() {
+        setWidth("100%");
+        setHeight("98%");
+        setBackgroundColor("#7f7f7f");
+        setOpacity(50);
+        setPosition(Positioning.RELATIVE);
+
+        parent.addChild(this);
+        parent.addChild(dialog);
+        dialog.moveTo(0, 20);
+        moveTo(0, 7);
+    }
+
+
+    public void setArtifact(Artifact artifact) {
+        this.artifact = artifact;
+    }
+
+
+    protected VLayout createDialog() {
+        title.setStyleName("loading-title");
+        title.setHeight(25);
+        title.setWidth100();
+
+        msg.setStyleName("loading-message");
+        msg.setValign(VerticalAlignment.TOP);
+        msg.setWidth100();
+        msg.setHeight(100);
+
+        ImageResource res = IMAGES.loading();
+        Img img = new Img(res.getURL(), 25, 25);
+
+        VLayout box = new VLayout();
+        box.setStyleName("loading-box");
+        box.setAlign(VerticalAlignment.TOP);
+        box.setDefaultLayoutAlign(VerticalAlignment.TOP);
+        box.addMember(title);
+        box.addMember(msg);
+        box.setMembersMargin(0);
+        box.setHeight(125);
+        box.setWidth(275);
+
+        dialog = new VLayout();
+        dialog.setAlign(Alignment.CENTER);
+        dialog.setDefaultLayoutAlign(Alignment.CENTER);
+        dialog.setMembersMargin(5);
+        dialog.setHeight100();
+        dialog.setWidth100();
+
+        dialog.addMember(img);
+        dialog.addMember(box);
+
+        return dialog;
+    }
+
+
+    private void startTimer() {
+        Timer t = new Timer() {
+            @Override
+            public void run() {
+                update();
+            }
+        };
+        t.schedule(UPDATE_INTERVAL);
+    }
+
+
+    protected void update() {
+        updateMessage();
+
+        final Config config = Config.getInstance();
+        final String url    = config.getServerUrl();
+        final String locale = config.getLocale();
+
+        describe.describe(url, locale, artifact, new AsyncCallback<Artifact>() {
+            public void onFailure(Throwable t) {
+                GWT.log("Error while DESCRIBE artifact: " + t.getMessage());
+                // SC.warn()...
+                startTimer();
+            }
+
+            public void onSuccess(Artifact artifact) {
+                GWT.log("Successfully DESCRIBE artifact.");
+
+                setArtifact(artifact);
+
+                if (artifact.isInBackground()) {
+                    startTimer();
+                }
+                else {
+                    finish();
+                }
+            }
+        });
+    }
+
+
+    protected void updateMessage() {
+        List<CalculationMessage> messages = artifact.getBackgroundMessages();
+        if (messages != null && messages.size() > 0) {
+            CalculationMessage calcMsg = messages.get(0);
+            title.setContents(getStepTitle(calcMsg));
+            msg.setContents(calcMsg.getMessage());
+        }
+        else {
+            title.setContents(MSG.calculationStarted());
+        }
+    }
+
+
+    protected String getStepTitle(CalculationMessage msg) {
+        return MSG.step() + " " + msg.getCurrentStep() + "/" + msg.getSteps();
+    }
+
+
+    private void finish() {
+        parent.removeChild(this);
+        parent.removeChild(dialog);
+        parent.setArtifact(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/LocationDistancePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,1252 @@
+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.smartgwt.client.data.Criteria;
+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.CellFormatter;
+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.events.TabSelectedHandler;
+import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
+
+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.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;
+import de.intevation.flys.client.client.event.FilterHandler;
+import de.intevation.flys.client.client.event.StringFilterEvent;
+import de.intevation.flys.client.client.ui.range.DistanceInfoDataSource;
+
+
+/**
+ * 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, FilterHandler
+{
+    /** 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;
+
+    /** The table filter.*/
+    protected TableFilter filter;
+
+    /**
+     * Creates a new LocationDistancePanel instance.
+     */
+    public LocationDistancePanel() {
+        distanceTable  = new ListGrid();
+        distanceTable.setAutoFetchData(true);
+
+        locationsTable = new ListGrid();
+        locationsTable.setAutoFetchData(true);
+
+        locationDistanceTable = new ListGrid();
+        locationDistanceTable.setAutoFetchData(true);
+
+        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);
+
+        createLocationTableDistance ();
+        createDistanceTable();
+        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 distance values.
+     */
+    protected void createDistanceTable() {
+
+        distanceTable.setWidth100();
+        distanceTable.setShowRecordComponents(true);
+        distanceTable.setShowRecordComponentsByCell(true);
+        distanceTable.setHeight100();
+        distanceTable.setEmptyMessage(MESSAGES.empty_filter());
+        distanceTable.setCanReorderFields(false);
+
+        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();
+                    try {
+                        double min = Double.parseDouble(r.getAttribute("from"));
+                        double max = Double.parseDouble(r.getAttribute("to"));
+                        setDistanceValues(min, max);
+                    }
+                    catch(NumberFormatException nfe) {
+                        // Is there anything to do?
+                    }
+                }
+                else {
+                    double[] selected;
+                    Record r = e.getRecord();
+                    double min = 0, max = 0;
+                    try {
+                        min = Double.parseDouble(r.getAttribute("from"));
+                        max = Double.parseDouble(r.getAttribute("to"));
+                    }
+                    catch(NumberFormatException nfe) {
+                        // Is there anything to do?
+                    }
+                    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.setCellFormatter(new CellFormatter() {
+            public String format(
+                Object value,
+                ListGridRecord record,
+                int rowNum, int colNum) {
+                    if (value == null) return null;
+                    try {
+                        NumberFormat nf;
+                        double v = Double.parseDouble((String)value);
+                        nf = NumberFormat.getFormat("###0.00##");
+                        return nf.format(v);
+                    }
+                    catch (Exception e) {
+                        return value.toString();
+                    }
+                }
+            }
+        );
+
+        from.setWidth("12%");
+        ListGridField to = new ListGridField("to", MESSAGES.to());
+        to.setType(ListGridFieldType.FLOAT);
+        to.setCellFormatter(new CellFormatter() {
+            public String format(
+                Object value,
+                ListGridRecord record,
+                int rowNum, int colNum) {
+                    if (value == null) return null;
+                    try {
+                        NumberFormat nf;
+                        double v = Double.parseDouble((String)value);
+                        nf = NumberFormat.getFormat("###0.00##");
+                        return nf.format(v);
+                    }
+                    catch (Exception e) {
+                        return value.toString();
+                    }
+                }
+            }
+        );
+
+        to.setWidth("12%");
+        ListGridField dside = new ListGridField("riverside",
+                MESSAGES.riverside());
+        dside.setType(ListGridFieldType.TEXT);
+        dside.setWidth("12%");
+
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth("10%");
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth("10%");
+
+        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_filter());
+        locationsTable.setCanReorderFields(false);
+
+        ListGridField addLocation = new ListGridField ("", "");
+        addLocation.setType (ListGridFieldType.ICON);
+        addLocation.setWidth (20);
+
+        addLocation.addRecordClickHandler (new RecordClickHandler () {
+            public void onRecordClick (RecordClickEvent e) {
+                Record record = e.getRecord();
+                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];
+                    }
+                    try {
+                        selected[val.length] =
+                            Double.parseDouble(record.getAttribute("from"));
+                    }
+                    catch(NumberFormatException nfe) {
+                        // Is there anything to do here?
+                    }
+                }
+                else {
+                    selected = new double[1];
+                    selected[0] =
+                        Double.parseDouble(record.getAttribute("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("12%");
+        ListGridField loc = new ListGridField("from", MESSAGES.locations());
+        loc.setType(ListGridFieldType.FLOAT);
+        loc.setWidth("12%");
+
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth("10%");
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth("10%");
+
+        locationsTable.setFields(addLocation, ldescr, loc, lside, bottom, top);
+    }
+
+
+    /**
+     * This method creates a table that contains the location values.
+     */
+    protected void createLocationTableDistance (){
+        locationDistanceTable.setWidth100();
+        locationDistanceTable.setShowRecordComponents(true);
+        locationDistanceTable.setShowRecordComponentsByCell(true);
+        locationDistanceTable.setHeight100();
+        locationDistanceTable.setEmptyMessage(MESSAGES.empty_filter());
+        locationDistanceTable.setCanReorderFields(false);
+
+        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 ());
+
+        locationDistanceTable.addCellClickHandler (new CellClickHandler () {
+            public void onCellClick (CellClickEvent e) {
+                if (e.getColNum() == 0) {
+                    Record r = e.getRecord ();
+                    try {
+                        double fromvalue =
+                             Double.parseDouble(r.getAttribute("from"));
+                        double tovalue = getTo ();
+                        setDistanceValues (fromvalue, tovalue);
+                    }
+                    catch(NumberFormatException nfe) {
+                        // Is there anything to do in here?
+                    }
+                }
+                else if (e.getColNum() == 1) {
+                    Record r = e.getRecord ();
+                    try {
+                        double fromvalue = getFrom ();
+                        double tovalue =
+                            Double.parseDouble(r.getAttribute("from"));
+                        setDistanceValues (fromvalue, tovalue);
+                    }
+                    catch(NumberFormatException nfe) {
+                        // Is there anything to do in here?
+                    }
+                }
+            }
+        });
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth("10%");
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth("10%");
+
+        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("12%");
+        ListGridField loc = new ListGridField("from", MESSAGES.locations());
+        loc.setType(ListGridFieldType.FLOAT);
+        loc.setWidth("12%");
+
+        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();
+        inputTables.addTabSelectedHandler(new TabSelectedHandler() {
+            public void onTabSelected(TabSelectedEvent evt) {
+                filter.clear();
+            }
+        });
+
+        Tab locations = new Tab(MESSAGES.locations());
+        Tab distances = new Tab(MESSAGES.distance());
+
+        inputTables.setWidth100();
+        inputTables.setHeight100();
+
+        locations.setPane(locationDistanceTable);
+        distances.setPane(distanceTable);
+
+        inputTables.addTab(locations);
+        inputTables.addTab(distances);
+
+        filter = new TableFilter();
+        filter.setHeight("30px");
+        filter.addFilterHandler(this);
+
+        inputTables.setHeight("*");
+
+        VLayout helper = new VLayout();
+        helper.addMember(inputTables);
+        helper.addMember(filter);
+        helper.setHeight100();
+        helper.setWidth100();
+
+        helperContainer.addMember(helper);
+
+        return layout;
+    }
+
+
+    public void onFilterCriteriaChanged(StringFilterEvent event) {
+        String search = event.getFilter();
+
+        if (search != null && search.length() > 0) {
+            Criteria c = new Criteria("description", search);
+
+            locationsTable.filterData(c);
+            distanceTable.filterData(c);
+            locationDistanceTable.filterData(c);
+        }
+        else {
+            locationsTable.clearCriteria();
+            distanceTable.clearCriteria();
+            locationDistanceTable.clearCriteria();
+        }
+    }
+
+
+
+
+    @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() {
+        List<Data> data = new ArrayList<Data>();
+
+        // 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);
+            }
+
+            Data dLocations = getDataLocations();
+            DataItem dFrom  = new DefaultDataItem("ld_from", "ld_from", "");
+            DataItem dTo    = new DefaultDataItem("ld_to", "ld_to", "");
+            DataItem dStep  = new DefaultDataItem("ld_step", "ld_step", "");
+
+            data.add(dLocations);
+            data.add(new DefaultData(
+                "ld_from", null, null, new DataItem[] { dFrom } ));
+            data.add(new DefaultData(
+                "ld_to", null, null, new DataItem[] { dTo } ));
+            data.add(new DefaultData(
+                "ld_step", null, null, new DataItem[] { dStep } ));
+        }
+        else {
+            Canvas member = container.getMember(0);
+            if (member instanceof DoubleRangePanel) {
+                DoubleRangePanel form = (DoubleRangePanel) member;
+                saveDistanceValues(form);
+            }
+
+            Data dFrom   = getDataFrom();
+            Data dTo     = getDataTo();
+            Data dStep   = getDataStep();
+            DataItem loc = new DefaultDataItem("ld_locations", "ld_locations","");
+
+            data.add(dFrom);
+            data.add(dTo);
+            data.add(dStep);
+            data.add(new DefaultData(
+                "ld_locations", null, null, new DataItem[] { loc } ));
+        }
+
+        Data dMode = getDataMode();
+        if (dMode != null) {
+            data.add(dMode);
+        }
+
+        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();
+            filter.clear();
+            // Remove the tab containing the locationDistanceTable.
+            // The 'updateTab()' avoids the tab content to be destroyed.
+            inputTables.updateTab(0, null);
+            inputTables.removeTab(0);
+
+            // Create a new tab containing the locationTable
+            Tab t1 = new Tab (MESSAGES.locations());
+            t1.setPane(locationsTable);
+            inputTables.addTab(t1, 0);
+
+            // Bring this tab to front.
+            inputTables.selectTab(0);
+        }
+        else {
+            enableDistancePanel();
+            filter.clear();
+            // Remove the tab containing the locationTable.
+            // The 'updateTab()' avoids the tab content to be destroyed.
+            inputTables.updateTab(0, null);
+            inputTables.removeTab(0);
+
+            //Create a new tab containing the locationDistanceTable.
+            Tab t1 = new Tab(MESSAGES.locations());
+            t1.setPane(locationDistanceTable);
+            inputTables.addTab(t1, 0);
+
+            // Bring the distanceTable tab to front.
+            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();
+                        }
+                    }
+                }
+            }
+        }
+
+        distanceTable.setDataSource(new DistanceInfoDataSource(
+            url, river, "distances"));
+        locationsTable.setDataSource(new DistanceInfoDataSource(
+            url, river, "locations"));
+        locationDistanceTable.setDataSource(new DistanceInfoDataSource(
+            url, river, "locations"));
+    }
+
+
+/*    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:51 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:51 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.addMember(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:51 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/NoInputPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.Canvas;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataList;
+
+
+public class NoInputPanel
+extends      AbstractUIProvider
+{
+    @Override
+    public Canvas create(DataList data) {
+        return new Canvas();
+    }
+
+
+    @Override
+    public Canvas createOld(DataList dataList) {
+        return new Canvas();
+    }
+
+
+    @Override
+    protected Data[] getData() {
+        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/OutputTab.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,61 @@
+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.Artifact;
+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 CollectionView. */
+    protected CollectionView collectionView;
+
+
+    /**
+     * 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,
+        CollectionView collectionView,
+        OutputMode     mode
+    ) {
+        super(title);
+
+        this.collection     = collection;
+        this.mode           = mode;
+        this.collectionView = collectionView;
+
+        setPane(new Label("Implement concrete subclasses to vary the output."));
+    }
+
+
+    public CollectionView getCollectionView() {
+        return collectionView;
+    }
+
+
+    public String getOutputName() {
+        return mode.getName();
+    }
+
+
+    public Artifact getArtifact() {
+        return getCollectionView().getArtifact();
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,876 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+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.types.VisibilityMode;
+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.SectionStack;
+import com.smartgwt.client.widgets.layout.SectionStackSection;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.Tab;
+import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
+
+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.AdvanceHandler;
+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, AdvanceHandler
+{
+    public static final String STYLENAME_OLD_PARAMETERS = "oldParameters";
+
+    /** 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 Map<String, Canvas> oldStorage;
+    protected DataList   current;
+
+    protected UIProvider uiProvider;
+
+    protected VLayout topLayout;
+    protected VLayout oldItems;
+    protected VLayout currentItems;
+    protected VLayout exportModes;
+    protected VLayout report;
+    protected VLayout helperPanel;
+    protected VLayout 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>();
+        oldStorage        = new TreeMap<String, Canvas>();
+        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>();
+        oldStorage        = new TreeMap<String, Canvas>();
+        topLayout         = new VLayout();
+        oldItems          = new VLayout();
+        currentItems      = new VLayout();
+        exportModes       = new VLayout();
+        report            = new VLayout();
+
+        init();
+
+        addParameterChangeHandler(this);
+
+        setArtifact(artifact, false);
+    }
+
+
+    protected void init() {
+        HLayout rootLayout = new HLayout();
+        rootLayout.setMembersMargin(20);
+
+        VLayout left = new VLayout();
+
+        if (old == null || old.size() == 0) {
+            oldItems.setHeight(1);
+        }
+
+        oldItems.setMembersMargin(10);
+        oldItems.setStyleName(STYLENAME_OLD_PARAMETERS);
+        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);
+
+        rootLayout.addMember(left);
+        rootLayout.addMember(createSectionStack());
+
+        topLayout.addMember(rootLayout);
+        if (artifact == null) {
+            Canvas moduleSelection = renderNew();
+            moduleSelection.setLayoutAlign(VerticalAlignment.TOP);
+            currentItems.addMember(moduleSelection);
+        }
+
+        setPane(topLayout);
+    }
+
+
+    protected SectionStack createSectionStack() {
+        SectionStack stack = new SectionStack();
+        stack.setHeight100();
+        stack.setCanResizeSections(true);
+        stack.setVisibilityMode(VisibilityMode.MULTIPLE);
+
+        // This canvas is used to render helper widgets
+        final SectionStackSection helperSection = new SectionStackSection();
+        helperSection.setExpanded(false);
+        helperSection.setTitle(MSG.helperPanelTitle());
+        helperPanel = new VLayout() {
+            public void addMember(Canvas component) {
+                super.addMember(component);
+                helperSection.setExpanded(true);
+            }
+
+            public void removeMembers(Canvas[] components) {
+                super.removeMembers(components);
+                helperSection.setExpanded(false);
+            }
+        };
+        helperPanel.setWidth100();
+        helperPanel.setHeight100();
+        helperSection.setItems(helperPanel);
+
+        // This canvas is used to render calculation results
+        final SectionStackSection tableSection = new SectionStackSection();
+        tableSection.setExpanded(false);
+        tableSection.setTitle(MSG.calcTableTitle());
+        tablePanel = new VLayout() {
+            public void addMember(Canvas component) {
+                super.addMember(component);
+                tableSection.setExpanded(true);
+            }
+
+            public void removeMembers(Canvas[] components) {
+                super.removeMembers(components);
+                tableSection.setExpanded(false);
+            }
+        };
+        tablePanel.setHeight100();
+        tablePanel.setWidth100();
+        tableSection.setItems(tablePanel);
+
+        stack.setSections(helperSection, tableSection);
+
+        return stack;
+    }
+
+
+    protected void setArtifact(Artifact artifact) {
+        setArtifact(artifact, true);
+    }
+
+
+    protected void setArtifact(Artifact artifact, boolean forward) {
+        Artifact tmp  = this.artifact;
+        this.artifact = artifact;
+
+        if (forward) {
+            fireParameterChangeEvent(
+                tmp, this.artifact, ParameterChangeEvent.Type.FORWARD);
+        }
+        else {
+            fireParameterChangeEvent(
+                tmp, this.artifact, ParameterChangeEvent.Type.BACK);
+        }
+    }
+
+
+    /**
+     * 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,
+        ParameterChangeEvent.Type type)
+    {
+        ParameterChangeEvent e = new ParameterChangeEvent(old, newArt, type);
+
+        for (ParameterChangeHandler handler: parameterHandlers) {
+            handler.onParameterChange(e);
+        }
+    }
+
+
+    /**
+     * 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(), null,
+                    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.");
+                                    old.clear();
+                                    cView.addArtifactToCollection(artifact);
+                                    setArtifact(artifact);
+                                }
+                            });
+                        }
+                });
+            }
+        });
+
+        DataList list = new DataList();
+        list.add(data);
+
+        return widget.create(list);
+    }
+
+
+    protected void clearOldData() {
+        old.clear();
+    }
+
+
+    public void addOldData(DataList old) {
+        addOldData(old, true);
+    }
+
+
+    public void addOldData(DataList old, boolean redraw) {
+        if (old != null) {
+            this.old.add(old);
+        }
+
+        refreshOld(redraw);
+    }
+
+
+    public void addOldDatas(DataList[] old) {
+        addOldDatas(old, true);
+    }
+
+
+    public void addOldDatas(DataList[] old, boolean redraw) {
+        if (old != null && old.length > 0) {
+            for (DataList o: old) {
+                if (o == null) {
+                    continue;
+                }
+
+                if (!exists(o)) {
+                    GWT.log("Data '" + o.getLabel() + "' is new.");
+                    addOldData(o, false);
+                }
+            }
+
+            if (redraw) {
+                addOldData(null, true);
+            }
+
+            return;
+        }
+
+        addOldData(null, true);
+    }
+
+
+    public boolean exists(DataList data) {
+        if (data == null) {
+            return false;
+        }
+
+        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(boolean redrawAll) {
+        if (redrawAll) {
+            refreshAllOld();
+        }
+        else {
+            DataList dataList = old.get(old.size()-1);
+            String   state    = dataList.getState();
+
+            if (oldStorage.get(state) == null) {
+                String     provider   = dataList.getUIProvider();
+                UIProvider uiprovider = UIProviderFactory.getProvider(
+                    provider,
+                    flys.getCurrentUser());
+                ((HasStepBackHandlers) uiprovider).addStepBackHandler(this);
+
+                Canvas c = uiprovider.createOld(dataList);
+                if (c != null) {
+                    oldStorage.put(dataList.getState(), c);
+                    oldItems.addMember(c);
+                }
+            }
+        }
+
+        updateOldHeight();
+    }
+
+
+    protected void refreshAllOld() {
+        List<String> not = new ArrayList<String>();
+
+        for (DataList data: old) {
+            String state = data.getState();
+
+            Canvas c = oldStorage.get(state);
+
+            if (c != null) {
+                not.add(state);
+            }
+        }
+
+        Map<String, Canvas> newOld = new TreeMap<String, Canvas>();
+
+        Set<Map.Entry<String, Canvas>> entries = oldStorage.entrySet();
+        for (Map.Entry<String, Canvas> entry: entries) {
+            String state = entry.getKey();
+            Canvas value = entry.getValue();
+
+            if (not.indexOf(state) < 0) {
+                oldItems.removeMember(value);
+            }
+            else {
+                newOld.put(state, value);
+            }
+        }
+
+        oldStorage = newOld;
+    }
+
+
+    protected void updateOldHeight() {
+        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);
+            currentItems.addMember(c);
+        }
+        else if (uiProvider != null) {
+            Canvas c = uiProvider.create(null);
+            c.setLayoutAlign(VerticalAlignment.TOP);
+
+            currentItems.addMember(c);
+        }
+        else {
+            currentItems.setHeight(1);
+        }
+
+        Canvas[] members = currentItems.getMembers();
+        if (members == null || members.length == 0) {
+            currentItems.setHeight(1);
+        }
+        else {
+            int height = 0;
+
+            for (Canvas member: members) {
+                height += member.getHeight();
+            }
+
+            currentItems.setHeight(height);
+        }
+    }
+
+
+    /**
+     * 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.");
+                    old.clear();
+
+                    setArtifact(artifact, true);
+                }
+        });
+    }
+
+
+    /**
+     * 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();
+
+                    setArtifact(artifact, false);
+                }
+            }
+        );
+    }
+
+
+    public void onAdvance(final String target) {
+        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 to '" + target + "'");
+                    SC.warn(MSG.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Artifact artifact) {
+                    GWT.log("Successfully advanced to '" + target + "'");
+
+                    old.clear();
+
+                    setArtifact(artifact, true);
+                }
+            }
+        );
+    }
+
+
+    /**
+     * 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");
+
+        Canvas[] c = helperPanel.getMembers();
+        if (c != null && c.length > 0) {
+            helperPanel.removeMembers(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,
+                flys.getCurrentUser());
+
+            provider.setContainer(helperPanel);
+            provider.setArtifact(art);
+            provider.setCollection(cView.getCollection());
+            provider.setParameterList(this);
+
+            ((HasStepForwardHandlers) provider).addStepForwardHandler(this);
+            ((HasStepBackHandlers) provider).addStepBackHandler(this);
+
+            setCurrentData(currentData, provider);
+        }
+        else {
+            String[] reachable = desc.getReachableStates();
+            if (reachable != null && reachable.length > 0) {
+                // We have reached a final state with the option to step to
+                // further to a next state. But in the current state, no user
+                // data is required.
+                UIProvider ui = UIProviderFactory.getProvider("continue", null);
+                ui.setArtifact(art);
+                ui.setCollection(cView.getCollection());
+                ui.setParameterList(this);
+
+                ((ContinuePanel) ui).addAdvanceHandler(this);
+
+                setCurrentData(null, ui);
+            }
+            else {
+                // we have reached a final state with no more user input
+                setCurrentData(null, null);
+            }
+        }
+
+        addOldDatas(
+            desc.getOldData(),
+            event.getType() == ParameterChangeEvent.Type.BACK);
+    }
+
+
+    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));
+        }
+        else {
+            exportModes.setHeight(1);
+        }
+    }
+
+    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 setTable(TableDataPanel table) {
+        removeTable();
+
+        Canvas c = table.create();
+        c.setHeight100();
+        c.setWidth100();
+
+        tablePanel.addMember(c);
+    }
+
+
+    public boolean hasTable() {
+        Canvas[] members = tablePanel.getMembers();
+
+        return members != null && members.length > 0;
+    }
+
+
+    /**
+     * Removes the table from the parameter list.
+     */
+    public void removeTable() {
+        Canvas[] members = tablePanel.getMembers();
+
+        if (members != null && members.length > 0) {
+            tablePanel.removeMembers(members);
+        }
+    }
+
+
+    public void registerCollectionViewTabHandler (TabSelectedHandler tsh) {
+        this.cView.registerTabHandler (tsh);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,846 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+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.MenuItemSeparator;
+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.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Recommendation;
+
+import de.intevation.flys.client.client.event.FilterHandler;
+import de.intevation.flys.client.client.event.StringFilterEvent;
+
+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.ArtifactService;
+import de.intevation.flys.client.client.services.ArtifactServiceAsync;
+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;
+import de.intevation.flys.client.client.services.DescribeCollectionService;
+import de.intevation.flys.client.client.services.DescribeCollectionServiceAsync;
+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;
+
+
+/**
+ * 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, FilterHandler
+{
+    /** 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);
+
+    /** The DescribeCollectionService used to update the existing collection. */
+    protected DescribeCollectionServiceAsync describeCollectionService =
+        GWT.create(DescribeCollectionService.class);
+
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected ArtifactServiceAsync createArtifactService =
+        GWT.create(ArtifactService.class);
+
+    /** 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);
+
+    /** 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;
+
+    /** All user collections.*/
+    protected List<Collection> collections;
+
+    /** The collections visible in the grid.*/
+    protected List<Collection> filteredCollections;
+
+    /** The collection to clone*/
+    protected Collection cloneCollection;
+
+    /**
+     * 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;
+
+        filteredCollections = new ArrayList();
+        collections = new ArrayList();
+        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);
+        grid.setCanReorderFields(false);
+
+        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);
+            }
+        });
+
+        MenuItem clone = new MenuItem(messages.clone_project());
+        clone.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                cloneProject(record.getCollection());
+            }
+        });
+
+        menu.addItem(open);
+        menu.addItem(rename);
+        menu.addItem(clone);
+        menu.addItem(new MenuItemSeparator());
+        menu.addItem(del);
+
+        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);
+
+        TableFilter filter = new TableFilter();
+        filter.setHeight("30px");
+        filter.addFilterHandler(this);
+
+        addMember(titleWrapper);
+        addMember(gridWrapper);
+        addMember(filter);
+    }
+
+
+    public void onFilterCriteriaChanged(StringFilterEvent event) {
+        String search = event.getFilter();
+        if (search != null && search.length() > 0) {
+            // Filter the records.
+            filterCollections(search);
+        }
+        else {
+            filteredCollections.clear();
+            for(int i = 0; i < collections.size(); i++) {
+                filteredCollections.add(collections.get(i));
+            }
+            updateGrid();
+        }
+    }
+
+
+    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(final 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();
+                if(flys.getWorkspace().hasView(c.identifier())) {
+                    flys.getWorkspace().updateTitle(c.identifier(), c.getName());
+                }
+            }
+        });
+    }
+
+
+    /**
+     * 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.");
+
+                    updateGridDataSource(collections);
+                }
+            }
+        );
+    }
+
+    /**
+     * Delete all entries in the ListGrid.
+     */
+    protected void clearGrid() {
+        ListGridRecord[] records = grid.getRecords();
+
+        for (ListGridRecord record: records) {
+            grid.removeData(record);
+        }
+    }
+
+
+    /**
+     * Update the collections data source.
+     *
+     * First removes all collections to avoid duplicates, then add new entries.
+     *
+     * @param c Collections to set to the data source.
+     */
+    protected void updateGridDataSource (Collection[] c) {
+        collections.clear();
+        if(c == null) {
+            clearGrid();
+            return;
+        }
+        for (Collection coll : c) {
+            this.collections.add(coll);
+        }
+        filterCollections("");
+    }
+
+
+    /**
+     * Updates the ListGrid.
+     */
+    protected void updateGrid() {
+        clearGrid();
+
+        if (filteredCollections == null ||
+            filteredCollections.size() == 0) {
+            return;
+        }
+
+        for (Collection c: filteredCollections) {
+            grid.addData(new CollectionRecord(c));
+        }
+    }
+
+    /**
+     * Filter for the user collections.
+     *
+     * @param search String to search for in collection names.
+     */
+    protected void filterCollections(String search) {
+        int j = 0;
+
+        // Clear the collection list.
+        filteredCollections.clear();
+
+        // Filter the list.
+        for (int i = 0; i < collections.size(); i++) {
+            String name;
+
+            // Get the collection name.
+            if (collections.get(i).getName().equals("") ||
+                collections.get(i).getName() == null) {
+                name = collections.get(i).identifier();
+            }
+            else {
+                name = collections.get(i).getName();
+            }
+
+            name = name.toLowerCase();
+            // Add a collection to the filtered list if the search string
+            // matches.
+            if (name.contains(search.toLowerCase())) {
+                filteredCollections.add(collections.get(i));
+                j++;
+            }
+        }
+        updateGrid();
+    }
+
+
+    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;
+    }
+
+
+    protected void cloneProject(Collection c) {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        cloneCollection = c;
+
+        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.");
+                    String uuid = getMasterArtifact(newCollection);
+                    cloneArtifact(uuid);
+                }
+            }
+        );
+    }
+
+
+    protected String getMasterArtifact(Collection newCollection) {
+        String uuid = newCollection.getItem(0).identifier();
+        // The master artifact uuid.
+        return uuid;
+    }
+
+
+    protected void cloneArtifact(String uuid) {
+        Config config               = Config.getInstance();
+        final String url            = config.getServerUrl();
+        final String locale         = config.getLocale();
+
+        Recommendation recommendation = new Recommendation(
+            "winfo",
+            null,
+            uuid,
+            null);
+
+        String factory = recommendation.getFactory();
+        createArtifactService.create(
+            url, locale, factory, recommendation,
+            new AsyncCallback<Artifact>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Error loading recommendations: " +
+                        caught.getMessage());
+                }
+
+                public void onSuccess(Artifact artifact) {
+                    GWT.log("Created new artifact: " + artifact.getUuid());
+                    createCollection(artifact);
+                }
+            }
+        );
+    }
+
+
+    protected void createCollection(final Artifact artifact) {
+        Config config        = Config.getInstance();
+        final String url     = config.getServerUrl();
+        final String locale  = config.getLocale();
+        final String ownerid = user.identifier();
+
+        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.");
+                    addArtifactToCollection(artifact, collection);
+                }
+            }
+        );
+    }
+
+
+    protected void addArtifactToCollection(Artifact a, Collection c) {
+        Config config               = Config.getInstance();
+        final String url            = config.getServerUrl();
+        final String locale         = config.getLocale();
+
+        addArtifactService.add(
+            c, a, 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 newColl) {
+                    String name = cloneCollection.getName();
+                    if(name == null || name.equals("")) {
+                        name = cloneCollection.identifier();
+                    }
+
+                    newColl.setName(messages.copy_of() + ": " + name);
+
+                    updateCollectionName(newColl);
+                    if(cloneCollection.getTTL() == 0) {
+                        newColl.setTTL(0);
+                        updateCollectionTTL(newColl);
+                    }
+                    updateUserCollections();
+                }
+            }
+        );
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/RecommendationPairRecord.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,127 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import de.intevation.flys.client.shared.model.Recommendation;
+
+
+/**
+ * Two strings to be displayed in a GridList, derived from two
+ * Recommendations.
+ */
+public class RecommendationPairRecord extends ListGridRecord {
+
+    /** First attribute-name for StringPairRecord. */
+    protected static final String ATTRIBUTE_FIRST  = "first";
+    
+    /** Second attribute-name for StringPairRecord. */
+    protected static final String ATTRIBUTE_SECOND = "second";
+
+    /** The "first" recommendation (typically the minuend). */
+    Recommendation first;
+
+    /** The "second" recommendation (typically the subtrahend). */
+    Recommendation second;
+
+    /**
+     * Whether the RecommendationPairRecord was restored from data and thus
+     * already loaded (usually cloned) in an ArtifactCollection or not.
+     */
+    boolean alreadyLoaded;
+
+
+    /** Trivial, blocked constructor. */
+    private RecommendationPairRecord() {
+    }
+
+
+    /**
+     * Create a new RecommendationPairRecord.
+     *
+     * @param first  The first recommendation (typically the minuend).
+     * @param second The second recommendation (typically the subtrahend).
+     */
+    public RecommendationPairRecord(
+        Recommendation first,
+        Recommendation second)
+    {
+        setFirst(first);
+        setSecond(second);
+        alreadyLoaded = false;
+    }
+
+
+    /**
+     * Sets the first recommendation with info (minuend).
+     * @param first Recommendation to store.
+     */
+    public void setFirst(Recommendation first) {
+        this.first = first;
+        setAttribute(ATTRIBUTE_FIRST, first.getDisplayName());
+    }
+
+
+    /**
+     * Sets the second recommendation with info (subtrahend).
+     * @param second Recommendation to store.
+     */
+    public void setSecond(Recommendation second) {
+        this.second = second;
+        setAttribute(ATTRIBUTE_SECOND, second.getDisplayName());
+    }
+
+
+    /**
+     * Get first recommendation (typically the minuend).
+     * @return first recommendation (typically the minuend).
+     */
+    public Recommendation getFirst() {
+        return first;
+    }
+
+
+    /**
+     * Get second recommendation (typically the subtrahend).
+     * @return second recommendation (typically the subtrahend).
+     */
+    public Recommendation getSecond() {
+        return second;
+    }
+
+
+    /**
+     * Get name of first recommendation (typically the minuend).
+     * @return name of first recommendation (typically the minuend).
+     */
+    public String getFirstName() {
+        return first.getDisplayName();
+    }
+
+
+    /**
+     * Get name of second recommendation (typically the subtrahend).
+     * @return name of second recommendation (typically the subtrahend).
+     */
+    public String getSecondName() {
+        return second.getDisplayName();
+    }
+
+
+    /**
+     * Sets whether or not the Recommendation is already loaded (in contrast
+     * to not yet loaded).
+     * @param isAlreadyLoaded new value.
+     */
+    public void setIsAlreadyLoaded(boolean isAlreadyLoaded) {
+        this.alreadyLoaded = isAlreadyLoaded;
+    }
+
+
+    /**
+     * Whether or not this pair of recommendations is already laoded (usually
+     * cloned) in an ArtifactCollection.
+     * @return whether pair of recommendations is already loaded.
+     */
+    public boolean isAlreadyLoaded() {
+        return this.alreadyLoaded;
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,188 @@
+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);
+            label.setWidth(400);
+
+            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:51 2012 +0200
@@ -0,0 +1,478 @@
+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.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.widgets.grid.CellFormatter;
+
+import com.smartgwt.client.data.Criteria;
+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.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;
+import de.intevation.flys.client.client.ui.range.DistanceInfoDataSource;
+import de.intevation.flys.client.client.event.FilterHandler;
+import de.intevation.flys.client.client.event.StringFilterEvent;
+
+
+/**
+ * This UIProvider creates a widget to enter locations.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class SingleLocationPanel
+extends      AbstractUIProvider
+implements   FilterHandler
+{
+    /** 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_filter());
+        locationTable.setCanReorderFields(false);
+
+        ListGridField addLocation = new ListGridField ("", "");
+        addLocation.setType (ListGridFieldType.ICON);
+        addLocation.setWidth (20);
+        addLocation.addRecordClickHandler (new RecordClickHandler () {
+            public void onRecordClick (RecordClickEvent e) {
+                Record record = e.getRecord();
+                double[] selected = new double[1];
+                try {
+                    selected[0] =
+                        Double.parseDouble(record.getAttribute("from"));
+                }
+                catch(NumberFormatException nfe) {
+                    // Is there anything else to do here?
+                }
+                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("10%");
+
+        ListGridField loc = new ListGridField("from", MESSAGES.location());
+        loc.setCellFormatter(new CellFormatter() {
+            public String format(
+                Object value,
+                ListGridRecord record,
+                int rowNum, int colNum) {
+                    if (value == null) return null;
+                    try {
+                        NumberFormat nf;
+                        double v = Double.parseDouble((String)value);
+                        nf = NumberFormat.getFormat("###0.00##");
+                        return nf.format(v);
+                    }
+                    catch (Exception e) {
+                        return value.toString();
+                    }
+                }
+            }
+        );
+        loc.setType(ListGridFieldType.FLOAT);
+
+        loc.setWidth("10%");
+
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth("10%");
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth("10%");
+
+        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) {}});
+
+        locationTable.setAutoFetchData(true);
+
+        container.addMember(locationPanel);
+
+        layout.addMember(container);
+
+        container.setMembersMargin(30);
+
+        TableFilter filter = new TableFilter();
+        filter.setHeight("30px");
+        filter.addFilterHandler(this);
+
+        helperContainer.addMember(locationTable);
+        helperContainer.addMember(filter);
+        createInputPanel();
+        return layout;
+    }
+
+    public void onFilterCriteriaChanged(StringFilterEvent event) {
+        String search = event.getFilter();
+
+        if (search != null && search.length() > 0) {
+            Criteria c = new Criteria("description", search);
+            locationTable.filterData(c);
+        }
+        else {
+            // TODO Remove filter
+        }
+    }
+
+    @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;
+
+        if (values.length > 1) {
+            errors.add(MESSAGES.too_many_values());
+        }
+
+        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+1];
+        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});
+        }
+        data[values.length] = new DefaultData(
+            "ld_mode",
+            null, null,
+            new DataItem[] {
+                new DefaultDataItem("ld_mode", "ld_mode", "locations") });
+
+        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;
+                        }
+                    }
+                }
+            }
+        }
+
+        locationTable.setDataSource(new DistanceInfoDataSource(
+            url, river, "locations"));
+    }
+
+
+    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/StyleEditorWindow.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,447 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.LinkedHashMap;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.widgets.Window;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.Button;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.CheckboxItem;
+import com.smartgwt.client.widgets.form.fields.ColorPickerItem;
+import com.smartgwt.client.widgets.form.fields.SelectItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.form.events.ItemChangedEvent;
+import com.smartgwt.client.widgets.form.events.ItemChangedHandler;
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.VerticalAlignment;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionItemAttribute;
+import de.intevation.flys.client.shared.model.Style;
+import de.intevation.flys.client.shared.model.StyleSetting;
+import de.intevation.flys.client.shared.model.FacetRecord;
+import de.intevation.flys.client.shared.model.Theme;
+
+import de.intevation.flys.client.client.services.CollectionItemAttributeServiceAsync;
+import de.intevation.flys.client.client.services.CollectionItemAttributeService;
+import de.intevation.flys.client.client.ui.ThemePanel;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.Config;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class StyleEditorWindow
+extends Window
+implements ClickHandler
+{
+    /** The interface that provides i18n messages. */
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** The collection. */
+    protected Collection collection;
+
+    /** The parent ThemePanel. */
+    protected ThemePanel panel;
+
+    /** The attributes. */
+    protected CollectionItemAttribute attributes;
+
+    /** The selected facet. */
+    protected FacetRecord facet;
+
+    /** Main layout. */
+    protected VLayout layout;
+
+    /** The service used to set collection item attributes. */
+    protected CollectionItemAttributeServiceAsync itemAttributeService =
+        GWT.create(CollectionItemAttributeService.class);
+
+
+    /**
+     * Setup editor dialog.
+     * @param collection The collection the current theme belongs to.
+     * @param attributes The collection attributes.
+     * @param facet      The selected facet.
+     */
+    public StyleEditorWindow (
+        Collection collection,
+        CollectionItemAttribute attributes,
+        FacetRecord facet)
+    {
+        this.collection = collection;
+        this.attributes = attributes;
+        this.facet = facet;
+        this.layout = new VLayout();
+
+        init();
+        initPanels();
+    }
+
+
+    /**
+     * Initialize the window and set the layout.
+     */
+    protected void init() {
+        setTitle(MSG.properties());
+        setCanDragReposition(true);
+        setCanDragResize(true);
+        layout.setMargin(10);
+
+        layout.setWidth100();
+        layout.setHeight100();
+    }
+
+
+    /**
+     * Initialize the static window content like buttons and main layout.
+     */
+    protected void initPanels() {
+        HLayout buttons = new HLayout();
+        Button accept = new Button(MSG.label_ok());
+        Button cancel = new Button(MSG.label_cancel());
+        cancel.addClickHandler(this);
+        accept.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent e) {
+                saveStyle();
+            }
+        });
+
+        buttons.addMember(accept);
+        buttons.addMember(cancel);
+        buttons.setAlign(Alignment.CENTER);
+        buttons.setHeight(30);
+
+        VLayout propGrid = createPropertyGrid();
+
+        layout.addMember(propGrid);
+        layout.addMember(buttons);
+        addItem(layout);
+        setWidth(300);
+        setHeight(410);
+    }
+
+
+    /**
+     * Setter for the parent panel.
+     * @param panel The panel.
+     */
+    public void setThemePanel (ThemePanel panel) {
+        this.panel = panel;
+    }
+
+
+    /**
+     * this method is called when the user aborts theming.
+     * @param event The event.
+     */
+    public void onClick(ClickEvent event) {
+        this.hide();
+    }
+
+
+    /**
+     * This method creates the property grid for available styling attributes.
+     * @return The layout containing the UI elements.
+     */
+    protected VLayout createPropertyGrid() {
+        VLayout properties = new VLayout();
+
+        Theme t = facet.getTheme();
+        Style s = attributes.getStyle(t.getFacet(), t.getIndex());
+
+        StaticTextItem name = new StaticTextItem("name", "Name");
+        name.setValue(facet.getName());
+        name.setTitleStyle("color:#000; width:120px");
+        name.setTitleAlign(Alignment.LEFT);
+        name.setDisabled(true);
+        name.setShowDisabled(false);
+        DynamicForm f = new DynamicForm();
+        f.setFields(name);
+        properties.addMember(f);
+
+        String[] sets = {"showlines",
+                         "showpoints",
+                         "linetype",
+                         "linesize",
+                         "linecolor",
+                         "font",
+                         "textstyle",
+                         "textsize",
+                         "textcolor",
+                         "textorientation",
+                         "backgroundcolor",
+                         "showbackground"};
+
+        for (int i = 0; i < sets.length; i ++) {
+            StyleSetting set = s.getSetting(sets[i]);
+
+            if (set == null) {
+                continue;
+            }
+
+            DynamicForm property = createPropertyUI(
+                set.getDisplayName(),
+                set.getName(),
+                set.getType(),
+                set.getDefaultValue());
+            properties.addMember(property);
+        }
+        return properties;
+    }
+
+
+    /**
+     * Create a property form.
+     * @param dname The display name.
+     * @param name The property name.
+     * @param type The property type.
+     * @param value The current value.
+     *
+     * @return The dynamic form for the attribute property.
+     */
+    protected DynamicForm createPropertyUI(
+        String dname,
+        String name,
+        String type,
+        String value)
+    {
+        DynamicForm df = new DynamicForm();
+
+        FormItem f;
+        if(type.equals("int")) {
+            f = new SelectItem(name, dname);
+            if (name.equals("linesize")) {
+                f = createLineSizeUI(f);
+            }
+            else if (name.equals("textsize")) {
+                LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
+                valueMap.put("3", "3");
+                valueMap.put("5", "5");
+                valueMap.put("8", "8");
+                valueMap.put("10", "10");
+                valueMap.put("12", "12");
+                valueMap.put("14", "14");
+                valueMap.put("18", "18");
+                valueMap.put("24", "24");
+                f.setValueMap(valueMap);
+            }
+            f.setValue(value);
+        }
+        else if (type.equals("boolean")) {
+            if(name.equals("textorientation")) {
+                f = new SelectItem(name, dname);
+                LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
+                valueMap.put("true", "horizontal");
+                valueMap.put("false", "vertical");
+                f.setValueMap(valueMap);
+                f.setValue(value);
+            }
+            else {
+                CheckboxItem c = new CheckboxItem(name, dname);
+                if(value.equals("true")) {
+                    c.setValue(true);
+                }
+                else {
+                    c.setValue(false);
+                }
+                c.setLabelAsTitle(true);
+                f = c;
+            }
+        }
+        else if (type.equals("Color")) {
+            ColorPickerItem c = new ColorPickerItem(name, dname);
+            c.setValue(rgbToHtml(value));
+            f = c;
+        }
+        else if (type.equals("Dash")) {
+            f = new SelectItem(name, dname);
+            LinkedHashMap<String, String> valueIcons = new LinkedHashMap<String, String>();
+            f.setImageURLPrefix(GWT.getHostPageBaseURL() + "images/linestyle-dash-");
+            f.setImageURLSuffix(".png");
+            f.setValueIconHeight(20);
+            f.setValueIconWidth(80);
+            LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
+            valueMap.put("10", "");
+            valueMap.put("10,5", "");
+            valueMap.put("20,10", "");
+            valueMap.put("30,10", "");
+            valueMap.put("20,5,15,5", "");
+            valueIcons.put("10", "10");
+            valueIcons.put("10,5", "10-5");
+            valueIcons.put("20,10", "20-10");
+            valueIcons.put("30,10", "30-10");
+            valueIcons.put("20,5,15,5", "20-5-15-5");
+            f.setValueIcons(valueIcons);
+            f.setValueMap(valueMap);
+            f.setValue(value);
+        }
+        else if (type.equals("Font")) {
+            f = new SelectItem(name, dname);
+            LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
+            valueMap.put("arial", "<span style='font-family:arial'>Arial</span>");
+            valueMap.put("courier", "<span style='font-family:courier'>Courier</span>");
+            valueMap.put("verdana", "<span style='font-family:verdana'>Verdana</span>");
+            valueMap.put("times", "<span style='font-family:times'>Times</span>");
+            f.setValueMap(valueMap);
+            f.setValue(value);
+        }
+        else if (type.equals("Style")) {
+            f = new SelectItem(name, dname);
+            LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
+            valueMap.put("standard", "<span style='font-style:normal'>Normal</span>");
+            valueMap.put("bold", "<span style='font-weight:bold'>Bold</span>");
+            valueMap.put("italic", "<span style='font-style:italic'>Italic</span>");
+            f.setValueMap(valueMap);
+            f.setValue(value);
+        }
+        else {
+            f = new FormItem();
+        }
+        f.setTitleStyle("color:#000; width:120px");
+        f.setTitleAlign(Alignment.LEFT);
+        df.setFields(f);
+        df.addItemChangedHandler(new ItemChangedHandler() {
+            public void onItemChanged(ItemChangedEvent e) {
+                String name = e.getItem().getName();
+                String newValue = e.getNewValue().toString();
+                setNewValue(name, newValue);
+            }
+        });
+
+        return df;
+    }
+
+
+    protected FormItem createLineSizeUI(FormItem f) {
+        LinkedHashMap<String, String> valueIcons = new LinkedHashMap<String, String>();
+        f.setImageURLPrefix(GWT.getHostPageBaseURL() + "images/linestyle-");
+        f.setImageURLSuffix("px.png");
+        f.setValueIconHeight(20);
+        f.setValueIconWidth(80);
+        LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
+        valueMap.put("1", "");
+        valueMap.put("2", "");
+        valueMap.put("3", "");
+        valueMap.put("4", "");
+        valueMap.put("5", "");
+        valueMap.put("6", "");
+        valueMap.put("7", "");
+        valueMap.put("8", "");
+        valueIcons.put("1", "1");
+        valueIcons.put("2", "2");
+        valueIcons.put("3", "3");
+        valueIcons.put("4", "4");
+        valueIcons.put("5", "5");
+        valueIcons.put("6", "6");
+        valueIcons.put("7", "7");
+        valueIcons.put("8", "8");
+        f.setValueIcons(valueIcons);
+        f.setValueMap(valueMap);
+        return f;
+    }
+
+
+    /**
+     * Static method to convert a color from RGB to HTML notation.
+     * @param rgb String in RGB notation.
+     *
+     * @return String in HTML notation.
+     */
+    protected static String rgbToHtml(String rgb) {
+        String[] parts = rgb.split(",");
+        int values[] = new int[parts.length];
+        for (int i = 0; i < parts.length; i++) {
+            parts[i] = parts[i].trim();
+            try {
+                values[i] = Integer.parseInt(parts[i]);
+            }
+            catch(NumberFormatException nfe) {
+                return "#000000";
+            }
+        }
+        String hex = "#";
+        for (int i = 0; i < values.length; i++) {
+           if (values[i] < 16) {
+                hex += "0";
+           }
+           hex += Integer.toHexString(values[i]);
+        }
+        return hex;
+    }
+
+
+    /**
+     * Static method to convert a color from HTML to RGB notation.
+     * @param html String in HTML notation.
+     *
+     * @return String in RGB notation.
+     */
+    protected static String htmlToRgb(String html) {
+        if (!html.startsWith("#")) {
+            return "0, 0, 0";
+        }
+
+        int r = Integer.valueOf(html.substring(1, 3), 16);
+        int g = Integer.valueOf(html.substring(3, 5), 16);
+        int b = Integer.valueOf(html.substring(5, 7), 16);
+
+        return r + ", " + g + ", " + b;
+    }
+
+
+    /**
+     * Saves the current style attributes and requests a redraw.
+     */
+    protected void saveStyle () {
+        GWT.log("StyleEditorWindow.saveStyle()");
+        Config config = Config.getInstance();
+        String url = config.getServerUrl();
+        String locale = config.getLocale();
+
+        itemAttributeService.setCollectionItemAttribute(
+            this.collection,
+            attributes.getArtifact(),
+            url,
+            locale,
+            attributes,
+            new AsyncCallback<Void>() {
+                public void onFailure (Throwable caught) {
+                    GWT.log("Could not set Collection item attributes.");
+                }
+                public void onSuccess(Void v) {
+                    GWT.log("Successfully saved collection item attributes.");
+                    panel.requestRedraw();
+                }
+            });
+
+
+        this.hide();
+    }
+
+
+    /**
+     * Sets a new value for an attribute.
+     * @param name Attribute name.
+     * @param value The new value.
+     */
+    protected final void setNewValue(String name, String value) {
+        Theme t = facet.getTheme();
+        Style s = attributes.getStyle(t.getFacet(), t.getIndex());
+        StyleSetting set = s.getSetting(name);
+        if(name.indexOf("color") != -1) {
+            value = htmlToRgb(value);
+        }
+        set.setDefaultValue(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/TableDataPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,181 @@
+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.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Canvas;
+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.types.ListGridFieldType;
+
+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);
+
+    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.
+     *
+     * @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);
+        dataTable.setCanDragSelectText(true);
+
+        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);
+                }
+            }
+        );
+
+        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) {
+        if (list == null || list.size() < 2) {
+            dataTable.setEmptyMessage(MESSAGES.error_no_calc_result());
+            dataTable.redraw();
+            return;
+        }
+
+        String[] header      = (String[])list.get(0);
+        String[] firstValues = (String[])list.get(1);
+
+        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]);
+
+            Config config = Config.getInstance();
+            String locale = config.getLocale();
+            try {
+                NumberFormat nf;
+                if (locale.equals("de")) {
+                    nf = NumberFormat.getFormat("#,##");
+                }
+                else {
+                    nf = NumberFormat.getFormat("#.##");
+                }
+                nf.parse(firstValues[i]);
+                f.setType(ListGridFieldType.FLOAT);
+            }
+            catch (NumberFormatException nfe) {
+                f.setType(ListGridFieldType.TEXT);
+            }
+
+        }
+
+        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/TableFilter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,98 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.TextItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
+import com.smartgwt.client.widgets.form.fields.events.KeyUpEvent;
+import com.smartgwt.client.widgets.form.fields.events.KeyUpHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import de.intevation.flys.client.client.event.FilterHandler;
+import de.intevation.flys.client.client.event.StringFilterEvent;
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class TableFilter
+extends      HLayout
+implements   ChangedHandler, KeyUpHandler
+{
+    /** The message class that provides i18n strings. */
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+    protected List<FilterHandler> handlers;
+
+    protected TextItem searchfield;
+
+
+    public TableFilter() {
+        super();
+        searchfield = new TextItem(MESSAGES.search());
+        handlers    = new ArrayList<FilterHandler>();
+
+        searchfield.addChangedHandler(this);
+        searchfield.addKeyUpHandler(this);
+
+        DynamicForm form = new DynamicForm();
+        form.setFields(searchfield);
+
+        addMember(form);
+    }
+
+
+    public void onChanged(ChangedEvent event) {
+        // This event handler is to slow...
+//        fireFilterCriteriaChanged(getSearchString());
+    }
+
+
+    public void onKeyUp(KeyUpEvent event) {
+        //To deactivate "As you type" filter add
+        // ' && event.getKeyName().equals("Enter")'
+        // to the if-clause.
+        if (event != null) {
+            fireFilterCriteriaChanged(getSearchString());
+        }
+    }
+
+
+    public String getSearchString() {
+        if (searchfield.getValueAsString() == null) {
+            return "";
+        }
+        else {
+            return searchfield.getValueAsString();
+        }
+    }
+
+
+    public void addFilterHandler(FilterHandler handler) {
+        if (handler != null) {
+            handlers.add(handler);
+        }
+    }
+
+
+    protected void fireFilterCriteriaChanged(String searchstring) {
+        StringFilterEvent filter = new StringFilterEvent(searchstring);
+
+        for (FilterHandler handler: handlers) {
+            handler.onFilterCriteriaChanged(filter);
+        }
+    }
+
+
+    public void clear() {
+        searchfield.clearValue();
+        fireFilterCriteriaChanged("");
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/TextProvider.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,198 @@
+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.data.DataSource;
+import com.smartgwt.client.data.DataSourceField;
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.FieldType;
+import com.smartgwt.client.types.TitleOrientation;
+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.events.ItemChangedEvent;
+import com.smartgwt.client.widgets.form.events.ItemChangedHandler;
+import com.smartgwt.client.widgets.form.validator.Validator;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+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;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class TextProvider
+extends      AbstractUIProvider
+implements   ItemChangedHandler
+{
+
+    public static final String FIELD_NAME  = "textprovider_inputfield";
+    public static final int    FORM_WIDTH  = 400;
+    public static final int    TITLE_WIDTH = 75;
+
+
+    protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected DynamicForm form;
+
+
+    @Override
+    public Canvas create(DataList dataList) {
+        Canvas form = createForm();
+
+        initDefaultValues(dataList);
+
+        return form;
+    }
+
+
+    protected Canvas createForm() {
+        return createForm(null);
+    }
+
+
+    protected Canvas createForm(String title) {
+        form = new DynamicForm();
+        form.addItemChangedHandler(this);
+        form.setTitlePrefix("");
+        form.setTitleSuffix(": ");
+        form.setTitleAlign(Alignment.LEFT);
+        form.setTitleOrientation(TitleOrientation.LEFT);
+        form.setTitleWidth(getTitleWidth());
+        form.setWidth(getFormWidth());
+
+        DataSourceField item = createField();
+        item.setTitle(title);
+
+        Validator validator = getValidator();
+        if (validator != null) {
+            item.setValidators(validator);
+        }
+
+        DataSource source = new DataSource();
+        source.setFields(item);
+
+        form.setDataSource(source);
+
+        return form;
+    }
+
+
+    protected void initDefaultValues(DataList dataList) {
+        Data     data = dataList.get(0);
+        DataItem item = data.getDefault();
+
+        String value = item.getStringValue();
+
+        form.setValue(getFieldName(), value);
+    }
+
+
+    protected DataSourceField createField() {
+        return new DataSourceField(getFieldName(), getFieldType());
+    }
+
+
+    /**
+     * Get field name.
+     * @return fields name (developer-centric).
+     */
+    protected String getFieldName() {
+        return FIELD_NAME;
+    }
+
+
+    /**
+     * Get field type.
+     * @return fields type.
+     */
+    protected FieldType getFieldType() {
+        return FieldType.TEXT;
+    }
+
+
+    protected Validator getValidator() {
+        return null;
+    }
+
+
+    protected int getFormWidth() {
+        return FORM_WIDTH;
+    }
+
+
+    protected int getTitleWidth() {
+        return TITLE_WIDTH;
+    }
+
+
+    /** @return null. */
+    protected String getDataName() {
+        return null;
+    }
+
+
+    protected String getValueAsString() {
+        return (String) form.getValue(getFieldName());
+    }
+
+
+    @Override
+    public Canvas createOld(DataList dataList) {
+        Data       data  = dataList.get(0);
+        DataItem[] items = data.getItems();
+
+        HLayout layout = new HLayout();
+        Label   label  = new Label(dataList.getLabel());
+        Label   value  = new Label(items[0].getLabel());
+
+        layout.setHeight(35);
+        layout.setWidth(400);
+        label.setWidth(200);
+
+        layout.addMember(label);
+        layout.addMember(value);
+        layout.addMember(getBackButton(dataList.getState()));
+
+        return layout;
+    }
+
+
+    @Override
+    public List<String> validate() {
+        List<String> errors = new ArrayList<String>();
+
+        if (!form.validate()) {
+            errors.add(MSG.wrongFormat());
+        }
+
+        return errors;
+    }
+
+
+    @Override
+    protected Data[] getData() {
+        String value = getValueAsString();
+        String name  = getDataName();
+
+        DataItem item = new DefaultDataItem(name, name, value);
+        return new Data[] { new DefaultData(
+            name, null, null, new DataItem[] { item }) };
+    }
+
+
+    @Override
+    public void onItemChanged(ItemChangedEvent event) {
+        form.validate();
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/ThemeNavigationPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,103 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.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/ThemePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,601 @@
+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.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+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.menu.Menu;
+import com.smartgwt.client.widgets.menu.MenuItem;
+import com.smartgwt.client.widgets.menu.events.ClickHandler;
+import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionItemAttribute;
+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.HasRedrawRequestHandlers;
+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.event.RedrawRequestHandler;
+import de.intevation.flys.client.client.event.RedrawRequestEvent;
+import de.intevation.flys.client.client.event.RedrawRequestEvent.Type;
+import de.intevation.flys.client.client.services.CollectionAttributeService;
+import de.intevation.flys.client.client.services.CollectionAttributeServiceAsync;
+import de.intevation.flys.client.client.services.CollectionItemAttributeService;
+import de.intevation.flys.client.client.services.CollectionItemAttributeServiceAsync;
+
+
+public abstract class ThemePanel
+extends               Canvas
+implements            OnMoveHandler,
+                      EditCompleteHandler,
+                      HasOutputParameterChangeHandlers,
+                      HasRedrawRequestHandlers
+{
+    protected CollectionAttributeServiceAsync updater =
+        GWT.create(CollectionAttributeService.class);
+
+    /** The service used to get collection item attributes. */
+    protected CollectionItemAttributeServiceAsync itemAttributeService =
+        GWT.create(CollectionItemAttributeService.class);
+
+    private FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** List of OutParameterChangedHandler. */
+    protected List<OutputParameterChangeHandler> outHandlers;
+    /** List of ChartShallRedrawHandler. */
+    protected List<RedrawRequestHandler> redrawRequestHandlers;
+
+    protected Collection collection;
+    protected OutputMode mode;
+
+    protected ThemeNavigationPanel navigation;
+    protected ListGrid list;
+
+    /** The collection view*/
+    protected CollectionView view;
+
+
+    /**
+     * Setup Grid, navigation bar.
+     * @param collection Collection for which to show themes.
+     */
+    public ThemePanel(Collection collection, OutputMode mode) {
+        this.collection  = collection;
+        this.mode        = mode;
+        this.list        = createGrid();
+        list.addRowContextClickHandler(new RowContextClickHandler() {
+            public void onRowContextClick(RowContextClickEvent event) {
+                ListGridRecord[] records = list.getSelection();
+
+                Menu menu = null;
+
+                if (records == null || records.length == 0) {
+                    return;
+                }
+                else if (records.length == 1) {
+                    menu = getSingleContextMenu(records);
+                }
+                else if (records.length > 1) {
+                    menu = getMultiContextMenu(records);
+                }
+
+                if (menu != null) {
+                    list.setContextMenu(menu);
+                    menu.showContextMenu();
+
+                    event.cancel();
+                }
+            }
+        });
+
+        this.redrawRequestHandlers = new ArrayList<RedrawRequestHandler>();
+        this.outHandlers = new ArrayList<OutputParameterChangeHandler>();
+        this.navigation  = new ThemeNavigationPanel();
+        this.navigation.addOnMoveHandler(this);
+    }
+
+
+    public abstract void activateTheme(Theme theme, boolean active);
+
+
+    /**
+     * 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();
+    }
+
+
+    /**
+     * Returns the ThemeList of the current collection and output mode.
+     *
+     * @return the current ThemeList.
+     */
+    public ThemeList getThemeList() {
+        return collection.getThemeList(mode.getName());
+    }
+
+
+    /**
+     * Registers a new OutputParameterChangeHandler.
+     *
+     * @param h The new handler.
+     */
+    public void addOutputParameterChangeHandler(OutputParameterChangeHandler h){
+        if (h != null) {
+            outHandlers.add(h);
+        }
+    }
+
+
+    /**
+     * Registers a RedrawRequestHandler.
+     *
+     * @param h The new handler.
+     */
+    public void addRedrawRequestHandler(RedrawRequestHandler h){
+        if (h != null) {
+            redrawRequestHandlers.add(h);
+        }
+    }
+
+
+    /**
+     * Request a redraw of e.g. a Chart.
+     */
+    final public void requestRedraw() {
+        for (RedrawRequestHandler handler: redrawRequestHandlers) {
+            handler.onRedrawRequest(new RedrawRequestEvent(Type.RESET));
+        }
+    }
+
+
+    /**
+     * Called when the attribution of an output changed. It informs the
+     * registered handlers about the changes.
+     */
+    protected void fireOutputParameterChanged() {
+        OutputParameterChangeEvent evt = new OutputParameterChangeEvent();
+
+        for (OutputParameterChangeHandler handler: outHandlers) {
+            handler.onOutputParameterChanged(evt);
+        }
+    }
+
+
+    public void setCollectionView(CollectionView view) {
+        this.view = view;
+    }
+
+
+    /**
+     * This method is used to clear the current theme grid and add new updated
+     * data.
+     */
+    protected void updateGrid() {
+        GWT.log("ThemePanel.updateGrid");
+
+        ListGridRecord[] selected = list.getSelectedRecords();
+
+        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 || theme.getVisible() == 0) {
+                continue;
+            }
+
+            FacetRecord newRecord = createRecord(theme);
+            addFacetRecord(newRecord);
+
+            String newArtifact = theme.getArtifact();
+            String newFacet    = theme.getFacet();
+            int    newIndex    = theme.getIndex();
+
+            for (ListGridRecord r: selected) {
+                FacetRecord sel = (FacetRecord) r;
+                Theme oldTheme  = sel.getTheme();
+
+                if (oldTheme.getArtifact().equals(newArtifact)
+                    && oldTheme.getFacet().equals(newFacet)
+                    && oldTheme.getIndex() == newIndex) {
+                    list.selectRecord(newRecord);
+                }
+            }
+        }
+
+        fireOutputParameterChanged();
+    }
+
+
+    protected void addFacetRecord(FacetRecord rec) {
+        list.addData(rec);
+    }
+
+
+    protected FacetRecord createRecord(Theme theme) {
+        return new FacetRecord(theme);
+    }
+
+
+    /**
+     * 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("ThemePanel.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();
+            }
+        });
+    }
+
+
+    /**
+     * Create and configure the Grid to display.
+     */
+    protected ListGrid createGrid() {
+        ListGrid grid = createNewGrid();
+
+        return grid;
+    }
+
+
+    protected ListGrid createNewGrid() {
+        return new ListGrid();
+    }
+
+
+    /**
+     * 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);
+        }
+    }
+
+
+    protected Menu getSingleContextMenu(final ListGridRecord[] records) {
+        Menu menu = new Menu();
+
+        menu.addItem(createActivateItem(records));
+        menu.addItem(createDeactivateItem(records));
+        menu.addItem(createRemoveItem(records));
+        menu.addItem(createPropertiesItem(records));
+
+        return menu;
+    }
+
+
+    protected Menu getMultiContextMenu(final ListGridRecord[] records) {
+        Menu menu = new Menu();
+
+        menu.addItem(createActivateItem(records));
+        menu.addItem(createDeactivateItem(records));
+        menu.addItem(createRemoveItem(records));
+
+        return menu;
+    }
+
+
+    protected MenuItem createPropertiesItem(final ListGridRecord[] records) {
+        MenuItem properties = new MenuItem(MSG.properties());
+
+        properties.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                GWT.log("clicked properties");
+                for (ListGridRecord record: records) {
+                    openStyleEditor((FacetRecord) record);
+                }
+            }
+        });
+
+        return properties;
+    }
+
+
+    protected MenuItem createActivateItem(final ListGridRecord[] records) {
+        MenuItem activate = new MenuItem(MSG.activateTheme());
+
+        activate.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                for (ListGridRecord record: records) {
+                    FacetRecord facet = (FacetRecord) record;
+                    activateTheme(facet.getTheme(), true);
+                }
+
+                updateCollection();
+            }
+        });
+
+        return activate;
+    }
+
+
+    protected MenuItem createDeactivateItem(final ListGridRecord[] records) {
+        MenuItem deactivate = new MenuItem(MSG.deactivateTheme());
+
+        deactivate.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                for (ListGridRecord record: records) {
+                    FacetRecord facet = (FacetRecord) record;
+                    activateTheme(facet.getTheme(), false);
+                }
+
+                updateCollection();
+            }
+        });
+
+        return deactivate;
+    }
+
+
+    protected MenuItem createRemoveItem(final ListGridRecord[] records) {
+        MenuItem remove = new MenuItem(MSG.removeTheme());
+
+        remove.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                for (ListGridRecord record: records) {
+                    FacetRecord facet = (FacetRecord) record;
+
+                    Theme theme = facet.getTheme();
+                    theme.setVisible(0);
+                    theme.setActive(0);
+                }
+
+                updateCollection();
+            }
+        });
+
+        return remove;
+    }
+
+
+    /**
+     * 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.
+     */
+    @Override
+    public void onEditComplete(EditCompleteEvent event) {
+        GWT.log("Edited record.");
+
+        int         row = event.getRowNum();
+        FacetRecord rec = (FacetRecord) list.getRecord(row);
+
+        Theme theme = rec.getTheme();
+
+        theme.setDescription(rec.getName());
+        activateTheme(theme, rec.getActive());
+
+        updateCollection();
+    }
+
+
+    /**
+     * This method should be defined in subclasses that wants to listen to this
+     * event.
+     *
+     * @param theme The theme that is moved.
+     * @param oldIdx The index of the theme before it was moved.
+     * @param newIdx The index of the theme after it was moved.
+     */
+    protected void fireThemeMoved(Theme theme, int oldIdx, int newIdx) {
+        // TODO Implement in subclasses
+    }
+
+
+    @Override
+    public void onMove(OnMoveEvent event) {
+        int type = event.getType();
+
+        GWT.log("ThemePanel.onMove: " + type);
+
+        ListGridRecord[] records = list.getSelection();
+
+        if (records == null || records.length == 0) {
+            GWT.log("ThemePanel.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();
+            fireThemeMoved(theme, theme.getPosition(), idx);
+            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();
+            fireThemeMoved(theme, theme.getPosition(), newPos[i]);
+            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();
+            fireThemeMoved(theme, theme.getPosition(), newPos[i]);
+            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();
+            fireThemeMoved(theme, theme.getPosition(), idx);
+            themeList.setThemePosition(theme, idx--);
+        }
+
+        updateGrid();
+    }
+
+
+    protected void openStyleEditor(final FacetRecord record) {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        String artifact = record.getTheme().getArtifact();
+
+        itemAttributeService.getCollectionItemAttribute(
+            this.collection,
+            artifact,
+            url,
+            locale,
+            new AsyncCallback<CollectionItemAttribute>() {
+                public void onFailure (Throwable caught) {
+                    SC.warn(MSG.getString(caught.getMessage()));
+                }
+                public void onSuccess(CollectionItemAttribute cia) {
+                    GWT.log("Successfully loaded collectionitem attributes.");
+                    showStyleEditor(cia, record);
+                }
+            });
+    }
+
+
+    protected void showStyleEditor(
+        CollectionItemAttribute cia,
+        FacetRecord record)
+    {
+        StyleEditorWindow win = new StyleEditorWindow(
+            collection,
+            cia,
+            record);
+        win.setThemePanel(this);
+        win.centerInPage();
+        win.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/Toolbar.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,57 @@
+package de.intevation.flys.client.client.ui;
+
+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.User;
+
+import de.intevation.flys.client.client.event.RedrawRequestHandler;
+
+
+public abstract class Toolbar extends HLayout {
+
+    protected OutputTab outputTab;
+
+
+    public Toolbar(OutputTab outputTab) {
+        super();
+
+        this.outputTab = outputTab;
+    }
+
+
+    public OutputTab getOutputTab() {
+        return outputTab;
+    }
+
+
+    public Artifact getArtifact() {
+        return outputTab.getCollectionView().getArtifact();
+    }
+
+
+    public User getUser() {
+        return outputTab.getCollectionView().getUser();
+    }
+
+
+    protected void openDatacageWindow(RedrawRequestHandler handler) {
+        Artifact artifact = getArtifact();
+        User     user     = getUser();
+
+        String outs = getOutputTab().getOutputName();
+
+        final DatacageWindow dc = new DatacageWindow(
+            artifact, user, outs, outputTab.getCollectionView());
+        dc.addRedrawRequestHandler(handler);
+        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/UIProvider.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,65 @@
+package de.intevation.flys.client.client.ui;
+
+import java.io.Serializable;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Collection;
+
+/**
+ * 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(VLayout 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);
+
+
+    /**
+     * Sets the parent Collection of the Artifact.
+     */
+    public void setCollection(Collection collection);
+
+    public void setParameterList(ParameterList 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/UIProviderFactory.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,69 @@
+package de.intevation.flys.client.client.ui;
+
+import de.intevation.flys.client.shared.model.User;
+
+public class UIProviderFactory {
+
+    private UIProviderFactory() {
+    }
+
+    public static UIProvider getProvider(String uiProvider, User user) {
+        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("distance_panel")) {
+            return new DistancePanel();
+        }
+        else if (uiProvider.equals("distance_only_panel")) {
+            return new DistanceOnlyPanel();
+        }
+        else if (uiProvider.equals("waterlevel_ground_panel")) {
+            return new WaterlevelGroundPanel();
+        }
+        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 if (uiProvider.equals("continue")) {
+            return new ContinuePanel();
+        }
+        else if (uiProvider.equals("wsp_datacage_panel")) {
+            return new WspDatacagePanel(user);
+        }
+        else if (uiProvider.equals("dgm_datacage_panel")) {
+            return new DemDatacagePanel(user);
+        }
+        else if (uiProvider.equals("datacage_twin_panel")) {
+            return new DatacageTwinPanel(user);
+        }
+        else if (uiProvider.equals("auto_integer")) {
+            return new AutoIntegerPanel();
+        }
+        else if (uiProvider.equals("boolean_panel")) {
+            return new BooleanPanel();
+        }
+        else if (uiProvider.equals("noinput")) {
+            return new NoInputPanel();
+        }
+        else if (uiProvider.equals("map_digitize")) {
+            return new DigitizePanel();
+        }
+        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:51 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:51 2012 +0200
@@ -0,0 +1,1452 @@
+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);
+
+    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() || qdTable.isLocked()) {
+                    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.addMember(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");
+
+        VLayout vLabel = null;
+
+        DataItem[] selItem = dSelection.getItems();
+        String     sel     = selItem != null
+            ? selItem[0].getStringValue()
+            : "";
+
+        if (sel.equals("single")) {
+            DataItem[] single = dSingle.getItems();
+
+            vLabel = wMode
+                ? createWString(single[0])
+                : createQString(single[0]);
+        }
+        else {
+            DataItem[] from = dFrom.getItems();
+            DataItem[] to   = dTo.getItems();
+            DataItem[] step = dStep.getItems();
+
+            vLabel = 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);
+        mLabel.setWidth(175);
+        mLabel.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 VLayout createWString(DataItem from, DataItem to, DataItem step) {
+        VLayout v = new VLayout();
+
+        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());
+
+        v.addMember(new Label(sb.toString()));
+
+        return v;
+    }
+
+
+    protected VLayout createWString(DataItem single) {
+        String  label = single.getLabel();
+        String[] cols = null;
+
+        if (label.indexOf(",") > 0) {
+            cols = label.split(",");
+        }
+        else {
+            cols = label.split(" ");
+        }
+
+        VLayout v = new VLayout();
+
+        for (String col: cols) {
+            Label l = new Label(col + " " + MESSAGE.unitWSingle());
+            l.setHeight(20);
+
+            v.addMember(l);
+        }
+
+        return v;
+    }
+
+
+    protected VLayout createQString(DataItem from, DataItem to, DataItem step) {
+        VLayout v = new VLayout();
+
+        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());
+
+        v.addMember(new Label(sb.toString()));
+
+        return v;
+    }
+
+
+    protected VLayout createQString(DataItem single) {
+        String  label = single.getLabel();
+        String[] cols = null;
+
+        if (label.indexOf(",") > 0) {
+            cols = label.split(",");
+        }
+        else {
+            cols = label.split(" ");
+        }
+
+        VLayout v = new VLayout();
+
+        for (String col: cols) {
+            Label l = new Label(col + " " + MESSAGE.unitQSingle());
+            l.setHeight(20);
+
+            v.addMember(l);
+        }
+
+        return v;
+    }
+
+
+    /**
+     * 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();
+        double step = panel.getStep();
+
+        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;
+        }
+
+        if (!errors.isEmpty()) {
+            panel.setValues(from, to, step);
+        }
+
+        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 getSingleData();
+        }
+        else {
+            Canvas member = container.getMember(0);
+            if (member instanceof DoubleRangePanel) {
+                DoubleRangePanel form = (DoubleRangePanel) member;
+
+                if (isWMode()) {
+                    saveRangeWValues(form);
+                }
+                else {
+                    saveRangeQValues(form);
+                }
+            }
+
+            return getRangeData();
+        }
+    }
+
+
+    /**
+     * Collects the required data for single mode and resets the data for range
+     * mode.
+     */
+    protected Data[] getSingleData() {
+        DataItem from = new DefaultDataItem("wq_from", "wq_from", "");
+        DataItem to   = new DefaultDataItem("wq_to", "wq_to", "");
+        DataItem step = new DefaultDataItem("wq_step", "wq_step", "");
+
+        return new Data[] {
+                getDataMode(),
+                getQMode(),
+                getDataSelectionMode(),
+                getDataSingle(),
+                new DefaultData(
+                    "wq_from",
+                    null,
+                    null,
+                    new DataItem[] {from}),
+                new DefaultData(
+                    "wq_to",
+                    null,
+                    null,
+                    new DataItem[] {to}),
+                new DefaultData(
+                    "wq_step",
+                    null,
+                    null,
+                    new DataItem[] {step}) };
+    }
+
+
+    /**
+     * Collects the required data for range mode and resets the data for single
+     * mode.
+     */
+    protected Data[] getRangeData() {
+        DataItem item = new DefaultDataItem("wq_single", "wq_single", "");
+
+        return new Data[] {
+                getDataMode(),
+                getQMode(),
+                getDataSelectionMode(),
+                getDataFrom(),
+                getDataTo(),
+                getDataStep(),
+                new DefaultData(
+                    "wq_single",
+                    null,
+                    null,
+                    new DataItem[] {item}) };
+    }
+
+
+    /**
+     * 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);
+        }
+
+        if (wqMode.equals("Q")) {
+            qdTable.hideIconFields();
+        }
+        else {
+            qdTable.showIconFields();
+            double[] empty = {};
+            valuesQ = empty;
+        }
+
+        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) {
+        ArtifactDescription adesc = artifact.getArtifactDescription();
+        return adesc.getKMRange();
+    }
+
+
+    /**
+     * 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) {
+        ArtifactDescription adesc = artifact.getArtifactDescription();
+        return adesc.getRiver();
+    }
+
+
+    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/WaterlevelGroundPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,111 @@
+package de.intevation.flys.client.client.ui;
+
+public class WaterlevelGroundPanel extends DistancePanel {
+
+    public static final String FIELD_LOWER = "diff_from";
+    public static final String FIELD_UPPER = "diff_to";
+    public static final String FIELD_STEP  = "diff_diff";
+
+
+    public WaterlevelGroundPanel() {
+        super("left");
+    }
+
+
+    @Override
+    protected String getLowerField() {
+        return FIELD_LOWER;
+    }
+
+
+    @Override
+    protected String getUpperField() {
+        return FIELD_UPPER;
+    }
+
+
+    @Override
+    protected String getStepField() {
+        return FIELD_STEP;
+    }
+
+
+    @Override
+    protected String labelFrom() {
+        return getLabelFrom() + " [" + getUnitFrom() + "]";
+    }
+
+
+    @Override
+    protected String getLabelFrom() {
+        return MSG.wgLabelFrom();
+    }
+
+
+    @Override
+    protected String getUnitFrom() {
+        return MSG.wgUnitFrom();
+    }
+
+
+    @Override
+    protected String labelTo() {
+        return getLabelTo() + " [" + getUnitTo() + "]";
+    }
+
+
+    @Override
+    protected String getLabelTo() {
+        return MSG.wgLabelTo();
+    }
+
+
+    @Override
+    protected String getUnitTo() {
+        return MSG.wgUnitTo();
+    }
+
+
+    @Override
+    protected String labelStep() {
+        return getLabelStep() + " [" + getUnitStep() + "]";
+    }
+
+
+    @Override
+    protected String getLabelStep() {
+        return MSG.wgLabelStep();
+    }
+
+
+    @Override
+    protected String getUnitStep() {
+        return MSG.wgUnitStep();
+    }
+
+
+    @Override
+    protected double getDefaultFrom() {
+        return 0;
+    }
+
+
+    @Override
+    protected double getDefaultTo() {
+        return 0;
+    }
+
+
+    @Override
+    protected double getDefaultStep() {
+        return 0;
+    }
+
+
+    @Override
+    protected void initHelperPanel() {
+        // We don't need a helper panel here. But we have to override this
+        // method to avoid the table creation in the parent class.
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/WspDatacagePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,159 @@
+package de.intevation.flys.client.client.ui;
+
+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.events.ClickEvent;
+
+import de.intevation.flys.client.shared.model.Artifact;
+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.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.Recommendation;
+import de.intevation.flys.client.shared.model.Recommendation.Facet;
+import de.intevation.flys.client.shared.model.Recommendation.Filter;
+import de.intevation.flys.client.shared.model.ToLoad;
+import de.intevation.flys.client.shared.model.User;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.StepForwardEvent;
+import de.intevation.flys.client.client.services.LoadArtifactService;
+import de.intevation.flys.client.client.services.LoadArtifactServiceAsync;
+
+
+public class WspDatacagePanel extends DatacagePanel {
+
+    public static final String WATERLEVEL_OUTS = "waterlevels";
+
+    public static final FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+
+    protected LoadArtifactServiceAsync loadService =
+        GWT.create(LoadArtifactService.class);
+
+    protected Recommendation recommendation;
+    protected Artifact       artifact;
+
+
+    public WspDatacagePanel() {
+        super();
+    }
+
+
+    public WspDatacagePanel(User user) {
+        super(user);
+    }
+
+
+    @Override
+    public String getOuts() {
+        return WATERLEVEL_OUTS;
+    }
+
+
+    @Override
+    protected void createWidget() {
+        super.createWidget();
+        widget.setIsMutliSelectable(false);
+    }
+
+
+    /**
+     * We need to override this method (defined in AbstractUIProvider) because
+     * we have to create a new Artifact specified by  the Datacage selection via
+     * Async request.
+     *
+     * @param The ClickEvent.
+     */
+    @Override
+    public void onClick(ClickEvent e) {
+        List<String> errors = validate();
+        if (errors == null || errors.isEmpty()) {
+            // 1) Fetch selected recommendation
+            Config config           = Config.getInstance();
+            final  String url       = config.getServerUrl();
+            final  String locale    = config.getLocale();
+            final  Collection c     = this.collection;
+            final  Recommendation r = getSelectedRecommendation();
+
+            // 2) Create, load Artifact and fire event
+            loadService.load(
+                c, r, "winfo", url, locale,
+                new AsyncCallback<Artifact>() {
+                    public void onFailure(Throwable caught) {
+                        SC.warn(MSG.getString(caught.getMessage()));
+                    }
+
+                    public void onSuccess(Artifact artifact) {
+                        fireStepForwardEvent(new StepForwardEvent(
+                            getData(r, artifact)));
+                    }
+                }
+            );
+        }
+        else {
+            showErrors(errors);
+        }
+    }
+
+
+    protected Recommendation getSelectedRecommendation() {
+        ToLoad toLoad = widget.getSelection();
+        List<Recommendation> recoms = toLoad.toRecommendations();
+
+        return recoms.get(0);
+    }
+
+
+    /**
+     * Nothing is done in this method. It returns null, because we serve the
+     * Data another way!
+     *
+     * @return always null!
+     */
+    @Override
+    protected Data[] getData() {
+        // do nothing here, the Data is fetched on another way in this panel.
+        return null;
+    }
+
+
+    protected Data[] getData(Recommendation r, Artifact newArtifact) {
+        String uuid = newArtifact.getUuid();
+        r.setMasterArtifact(uuid);
+
+        String value = createDataString(uuid, r.getFilter());
+
+        DataItem item = new DefaultDataItem(dataName, dataName, value);
+        return new Data[] { new DefaultData(
+            dataName, null, null, new DataItem[] { item }) };
+    }
+
+
+    protected String createDataString(String artifact, Filter filter) {
+        Facet f = null;
+
+        Map<String, List<Facet>>               outs = filter.getOuts();
+        Set<Map.Entry<String, List<Facet>>> entries = outs.entrySet();
+
+        for (Map.Entry<String, List<Facet>> entry: entries) {
+            List<Facet> fs = entry.getValue();
+
+            f = fs.get(0);
+            if (f != null) {
+                break;
+            }
+        }
+
+        return "[" + artifact + ";" + f.getName() + ";" + f.getIndex() + "]";
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,637 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+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.shared.model.ZoomObj;
+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.RedrawRequestHandler;
+import de.intevation.flys.client.client.event.RedrawRequestEvent;
+import de.intevation.flys.client.client.event.RedrawRequestEvent.Type;
+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,
+             RedrawRequestHandler
+{
+    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;
+
+    /** Transformer used to transform image pixels into chart coordinates. */
+    protected Transform2D[] transformer;
+
+    /** The collection view.*/
+    protected CollectionView view;
+
+    protected ChartThemePanel ctp;
+
+
+    /** 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 Stack<ZoomObj> zoomStack;
+    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.
+     * @param collectionView The shown collection.
+     */
+    public ChartOutputTab(
+        String         title,
+        Collection     collection,
+        OutputMode     mode,
+        CollectionView collectionView
+    ){
+        super(title, collection, collectionView, mode);
+
+        view      = collectionView;
+        left      = new Canvas();
+        right     = new Canvas();
+        xrange    = new int[2];
+        yrange    = new int[2];
+        zoom      = new double[4];
+        zoomStack = new Stack<ZoomObj>();
+
+        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);
+
+        // Output "cross_section" needs slightly modified ThemePanel
+        // (with action buttons).
+        if (mode.getName().equals("cross_section")) {
+            ctp = new CrossSectionChartThemePanel(collection, mode);
+        }
+        else {
+            ctp = new ChartThemePanel(collection, mode);
+            ctp.setCollectionView(this.view);
+        }
+
+        ctp.addRedrawRequestHandler(this);
+        ctp.addOutputParameterChangeHandler(this);
+
+        chart = createChartImg();
+        right.addChild(chart);
+        right.setOverflow(Overflow.HIDDEN);
+        //right.addChild(createChartPanel());
+        left.addChild(ctp);
+
+        tbarPanel = new ChartToolbar(collectionView, this);
+        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();
+    }
+
+
+    public void onRedrawRequest(RedrawRequestEvent event) {
+        if (event.getType() == Type.RESET) {
+            resetRanges();
+        }
+        else {
+            ctp.updateCollection();
+            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) {
+        zoomStack.push(new ZoomObj(zoom[0], zoom[1], zoom[2], zoom[3]));
+
+        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() {
+        zoomStack.push(new ZoomObj(zoom[0], zoom[1], zoom[2], zoom[3]));
+
+        zoom[0] = 0d;
+        zoom[1] = 1d;
+        zoom[2] = 0d;
+        zoom[3] = 1d;
+
+        updateChartInfo();
+        updateChartPanel();
+    }
+
+
+    /**
+     * This method zooms the current chart out by a given <i>factor</i>.
+     *
+     * @param factor The factor should be between 0-100.
+     */
+    public void zoomOut(int factor) {
+        if (factor < 0 || factor > 100 || chartInfo == null) {
+            return;
+        }
+
+        zoomStack.push(new ZoomObj(zoom[0], zoom[1], zoom[2], zoom[3]));
+
+        Axis xAxis = chartInfo.getXAxis(0);
+        Axis yAxis = chartInfo.getYAxis(0);
+
+        double[] x = zoomAxis(xAxis, factor);
+        double[] y = zoomAxis(yAxis, factor);
+
+        zoom[0] = x[0];
+        zoom[1] = x[1];
+        zoom[2] = y[0];
+        zoom[3] = y[1];
+
+        updateChartInfo();
+        updateChartPanel();
+    }
+
+
+    /**
+     * This method is used to zoom out. Zooming out is realizied with a stacked
+     * logic. Initially, you cannot zoom out. For each time you start a zoom-in
+     * action, the extent of the chart is stored and pushed onto a stack. A
+     * zoom-out will now lead you to the last extent that is poped from stack.
+     *
+     */
+    public void zoomOut() {
+        if (!zoomStack.empty()) {
+            zoom = zoomStack.pop().getZoom();
+
+            updateChartInfo();
+            updateChartPanel();
+        }
+    }
+
+
+    public double[] zoomAxis(Axis axis, int factor) {
+        GWT.log("Prepare Axis for zooming (factor: " + factor + ")");
+
+        double min   = axis.getMin();
+        double max   = axis.getMax();
+        double range = max > min ? max - min : min - max;
+
+        double curFrom = axis.getFrom();
+        double curTo   = axis.getTo();
+
+        double diff = curTo > curFrom ? curTo - curFrom : curFrom - curTo;
+
+        GWT.log("    max from    : " + min);
+        GWT.log("    max to      : " + max);
+        GWT.log("    max range   : " + range);
+        GWT.log("    current from: " + curFrom);
+        GWT.log("    current to  : " + curTo);
+        GWT.log("    current diff: " + diff);
+
+        double newFrom = curFrom - (diff * factor / 100);
+        double newTo   = curTo + (diff * factor / 100);
+
+        GWT.log("    new from: " + newFrom);
+        GWT.log("    new to  : " + newTo);
+
+        return new double[] {
+            (newFrom - min) / range,
+            (newTo - min) / range
+        };
+    }
+
+
+    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 String getExportUrl(int width, int height, String format) {
+        String url = getImgUrl(width, height);
+        url += "&format=" + format;
+        url += "&export=true";
+
+        return url;
+    }
+
+
+    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:51 2012 +0200
@@ -0,0 +1,97 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.OutputMode;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.ui.ThemePanel;
+
+import de.intevation.flys.client.client.services.FeedService;
+import de.intevation.flys.client.client.services.FeedServiceAsync;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartThemePanel extends ThemePanel {
+
+    /** The interface that provides i18n messages. */
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** The collection */
+    protected Collection collection;
+
+    public static final String GRID_FIELD_ACTIVE  = "active";
+    public static final String GRID_FIELD_NAME    = "name";
+    public static final String GRID_FIELD_ACTIONS = "actions";
+
+    FeedServiceAsync feedService = GWT.create(
+        de.intevation.flys.client.client.services.FeedService.class);
+
+    public ChartThemePanel(Collection collection, OutputMode mode) {
+        super(collection, mode);
+        this.collection = collection;
+
+        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);
+    }
+
+
+    @Override
+    public void activateTheme(Theme theme, boolean active) {
+        theme.setActive(active ? 1 : 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/client/ui/chart/ChartToolbar.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,189 @@
+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.Canvas;
+import com.smartgwt.client.widgets.ImgButton;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.ImgLink;
+import de.intevation.flys.client.client.ui.Toolbar;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartToolbar extends Toolbar {
+
+    protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected static FLYSImages IMG = GWT.create(FLYSImages.class);
+
+
+    public static final int PANEL_HEIGHT = 30;
+
+
+    protected ChartOutputTab chartTab;
+
+
+    protected Button datacage;
+
+    protected Canvas downloadPNG;
+
+    protected Canvas downloadPDF;
+
+    protected Canvas downloadSVG;
+
+    protected MousePositionPanel position;
+
+    protected ZoomboxControl zoombox;
+
+    protected ImgButton zoomToMaxExtent;
+
+    protected ImgButton historyBack;
+
+    protected ImgButton zoomOut;
+
+    protected PanControl panControl;
+
+
+
+    public ChartToolbar(CollectionView view, ChartOutputTab chartTab) {
+        super(chartTab);
+
+        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();
+        historyBack     = new ImgButton();
+        panControl      = new PanControl(chartTab, MSG.pan());
+
+        datacage.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'datacage' button.");
+                openDatacageWindow((ChartOutputTab) getOutputTab());
+            }
+        });
+
+        String baseUrl = GWT.getHostPageBaseURL();
+
+        downloadPNG = new ImgLink(
+            IMG.downloadPNG().getURL(),
+            chartTab.getExportUrl(1280, 1024, "png"),
+            20,
+            20);
+
+        downloadPDF = new ImgLink(
+            IMG.downloadPDF().getURL(),
+            chartTab.getExportUrl(1280, 1024, "pdf"),
+            20,
+            20);
+
+        downloadSVG = new ImgLink(
+            IMG.downloadSVG().getURL(),
+            chartTab.getExportUrl(1280, 1024, "svg"),
+            20,
+            20);
+
+        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);
+            }
+        });
+
+        historyBack.setSrc(baseUrl + MSG.zoom_back());
+        historyBack.setWidth(20);
+        historyBack.setHeight(20);
+        historyBack.setShowDown(false);
+        historyBack.setShowRollOver(false);
+        historyBack.setShowDisabled(false);
+        historyBack.setShowDisabledIcon(true);
+        historyBack.setShowDownIcon(false);
+        historyBack.setShowFocusedIcon(false);
+        historyBack.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                getChartOutputTab().zoomOut();
+            }
+        });
+
+        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 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(downloadPNG);
+        addMember(downloadPDF);
+        addMember(downloadSVG);
+        addMember(zoomToMaxExtent);
+        addMember(historyBack);
+        addMember(zoomOut);
+        addMember(zoombox);
+        addMember(panControl);
+        addMember(spacer);
+        addMember(position);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/CrossSectionChartThemePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,200 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.Alignment; 
+import com.smartgwt.client.widgets.Canvas;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.ListGridFieldType;
+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.HLayout;
+import com.smartgwt.client.widgets.form.fields.SpinnerItem;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.OutputMode;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DefaultArtifact;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.FacetRecord;
+
+import de.intevation.flys.client.client.services.FeedServiceAsync;
+
+
+/**
+ * ThemePanel much like ChartThemePanel, but shows an "Actions" column,
+ * needed for interaction in the CrossSection Charts.
+ */
+public class CrossSectionChartThemePanel
+extends      ChartThemePanel {
+
+    /**
+     * Trivial constructor.
+     */
+    public CrossSectionChartThemePanel(
+            Collection collection,
+            OutputMode mode)
+    {
+        super(collection, mode);
+    }
+
+
+    /**
+     * Create the handler for ChangeEvents on the Spinner in the
+     * facets that control km of cross section.
+     *
+     * @param feedService The FeedService to send (changed) data to.
+     * @param facetRecord The FacetRecord (~row in table) where this
+     *                    handler is added to (to a child, to be exact).
+     */
+    public final ChangedHandler createSpinnerHandler(
+        final FeedServiceAsync feedService,
+        final FacetRecord      facetRecord)
+    {
+        Config config          = Config.getInstance();
+        final String serverUrl = config.getServerUrl();
+        final String locale    = config.getLocale();
+
+        ChangedHandler handler = new ChangedHandler()
+            {
+                @Override
+                public void onChanged(ChangedEvent ce) {
+                    if (ce.getValue() == null) {
+                        return;
+                    }
+                    DefaultDataItem kmItem = new DefaultDataItem(
+                        "cross_section.km",
+                        "cross_section.km",
+                        ce.getValue().toString());
+                    DefaultData km = new DefaultData(
+                        "cross_section.km",
+                        null,
+                        null,
+                        new DataItem[] {kmItem});
+                    Data[] feedData = new Data[] {km};
+                    feedService.feed(serverUrl,
+                        locale,
+                        new DefaultArtifact(
+                            facetRecord.getTheme().getArtifact(),
+                            "TODO:hash"),
+                        feedData,
+                        new AsyncCallback<Artifact>() {
+                            public void onFailure(Throwable caught) {
+                                GWT.log("Could not feed artifact " + caught.getMessage());
+                                // TODO SC.warn 
+                            }
+                            public void onSuccess(Artifact artifact) {
+                                GWT.log("Successfully fed");
+                                // TODO: Also update content of spinnerbox
+                                requestRedraw();
+                            }
+                    });
+                }
+            };
+        return handler;
+    }
+
+
+    /**
+     * Create and configure the Grid to display.
+     */
+    @Override
+    protected ListGrid createGrid() {
+        ListGrid list = new ListGrid() {
+            @Override
+            protected Canvas createRecordComponent(
+                final ListGridRecord record,
+                Integer colNum)
+            {
+                    // Only cross_section Facets display an action widget.
+                    final FacetRecord facetRecord = (FacetRecord) record;
+                    if (!facetRecord.getTheme().getFacet().equals(
+                        "cross_section"))
+                    {
+                        return null;
+                    }
+
+                    String fieldName = this.getFieldName(colNum);
+
+                    if (fieldName.equals(GRID_FIELD_ACTIONS)) {
+                        HLayout recordCanvas = new HLayout(3);
+                        recordCanvas.setHeight(22);
+                        recordCanvas.setAlign(Alignment.CENTER);
+                        // TODO Refactor in createSpinner(data, artifact)
+                        SpinnerItem spinnerItem = new SpinnerItem();
+                        spinnerItem.setShowTitle(false);
+                        spinnerItem.setTitle("Waterlevel-Spinner");
+                        spinnerItem.setWidth(45);
+                        // TODO actually get the value from artifact
+                        // TODO actually get the range from artifact (or river?)
+                        spinnerItem.setDefaultValue(0);
+                        spinnerItem.setMin(0);
+                        spinnerItem.setMax(1000);
+                        spinnerItem.setStep(5f);
+                        spinnerItem.setChangeOnKeypress(true);
+
+                        spinnerItem.addChangedHandler(
+                            createSpinnerHandler(
+                                feedService,
+                                facetRecord));
+
+                        DynamicForm formWrap = new DynamicForm();
+                        formWrap.setFields(spinnerItem);
+                        formWrap.setTitlePrefix("");
+                        formWrap.setTitleSuffix("");
+                        recordCanvas.addMember(formWrap);
+                        return recordCanvas;
+                    }
+                    else {
+                        return null;
+                    }
+                }
+            };
+        list.setCanResizeFields(true);
+        list.setShowRecordComponents(true);
+        list.setShowRecordComponentsByCell(true);
+        list.setShowAllRecords(true);
+        list.setShowHeaderContextMenu(false);
+        return list;
+    }
+
+
+    /**
+     * Initializes the components (columns) of the theme grid.
+     */
+    @Override
+    protected void initGrid() {
+        list.setCanEdit(true);
+        list.setCanSort(false);
+        list.setShowRecordComponents(true);
+        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);
+
+        ListGridField actions = new ListGridField(GRID_FIELD_ACTIONS,
+             MSG.chart_themepanel_header_actions(), 50);
+
+        list.setFields(active, name, actions);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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:51 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/ZoomboxControl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,324 @@
+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[] { -1, -1 };
+        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);
+
+        Canvas chart = chartTab.getChartPanel();
+        chart.addMouseDownHandler(this);
+        chart.addMouseOutHandler(this);
+        chart.addMouseMoveHandler(this);
+        chart.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() {
+        Canvas chart = chartTab.getChartPanel();
+        chart.addChild(zoombox);
+
+        zoombox.setPosition(Positioning.ABSOLUTE);
+        zoombox.setBorder("2px solid black");
+        zoombox.setOpacity(50);
+        zoombox.setWidth(1);
+        zoombox.setHeight(1);
+        zoombox.setLeft(-10000);
+        zoombox.setTop(-10000);
+    }
+
+
+    /**
+     * 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()) - 1;
+        start[1] = getRelativeY(event.getY()) + 1;
+
+        end[0] = start[0];
+        end[1] = start[1];
+    }
+
+
+    /**
+     * 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() || !isZooming()) {
+            return;
+        }
+
+        int x = getRelativeX(event.getX());
+        int y = getRelativeY(event.getY());
+
+        end[0] = x > start[0] ? x-1 : x+1;
+        end[1] = y > start[1] ? y-1 : y+1;
+
+        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();
+
+        reset();
+    }
+
+
+    /**
+     * 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;
+        }
+
+        reset();
+    }
+
+
+    /**
+     * 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 true, if a zoom action is in process.
+     *
+     * @return true, if a zoom action is in process.
+     */
+    public boolean isZooming() {
+        return start[0] > 0 && start[1] > 0;
+    }
+
+
+    /**
+     * 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(-10000);
+        zoombox.setTop(-10000);
+        zoombox.setWidth(1);
+        zoombox.setHeight(1);
+    }
+
+
+    /**
+     * Resets the zoom control (start point and zoombox).
+     */
+    protected void reset() {
+        start[0] = -1;
+        start[1] = -1;
+
+        clearZoombox();
+    }
+
+
+    /**
+     * 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/map/DrawControl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,244 @@
+package de.intevation.flys.client.client.ui.map;
+
+import java.util.LinkedHashMap;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.SelectionType;
+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.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.SelectItem;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.Style;
+import org.gwtopenmaps.openlayers.client.control.Control;
+import org.gwtopenmaps.openlayers.client.control.DrawFeature;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener.FeatureAddedEvent;
+import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
+import org.gwtopenmaps.openlayers.client.handler.Handler;
+import org.gwtopenmaps.openlayers.client.handler.PathHandler;
+import org.gwtopenmaps.openlayers.client.handler.PolygonHandler;
+import org.gwtopenmaps.openlayers.client.layer.Vector;
+import org.gwtopenmaps.openlayers.client.util.Attributes;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.utils.EnableDisableCmd;
+
+
+public class DrawControl extends HLayout implements VectorFeatureAddedListener {
+
+    public static final String BARRIER_PIPE1    = "pipe1";
+    public static final String BARRIER_PIPE2    = "pipe2";
+    public static final String BARRIER_DITCH    = "ditch";
+    public static final String BARRIER_DAM      = "dam";
+    public static final String BARRIER_RINGDIKE = "ring_dike";
+
+    public static final String BARRIER_PIPE1_VALUE    = "Rohr 1";
+    public static final String BARRIER_PIPE2_VALUE    = "Rohr 2";
+    public static final String BARRIER_DITCH_VALUE    = "Graben";
+    public static final String BARRIER_DAM_VALUE      = "Damm";
+    public static final String BARRIER_RINGDIKE_VALUE = "Ringdeich";
+
+    public static final String FIELD_BARRIER_TYPE = "field_barrier_type";
+
+
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected EnableDisableCmd cmd;
+
+    protected ImgButton   button;
+    protected DynamicForm form;
+
+    protected Map    map;
+    protected Vector layer;
+
+    protected Control control;
+
+
+    public DrawControl(Map map, Vector layer, EnableDisableCmd cmd) {
+        this.map   = map;
+        this.layer = layer;
+        this.cmd   = cmd;
+
+        initialize();
+    }
+
+
+    protected void initialize() {
+        setWidth(100);
+        setMembersMargin(0);
+
+        button = new ImgButton();
+
+        String baseUrl = GWT.getHostPageBaseURL();
+        button.setSrc(baseUrl + MSG.digitize());
+        button.setActionType(SelectionType.CHECKBOX);
+        button.setSize(20);
+        button.setShowRollOver(false);
+        button.setSelected(false);
+        button.setTooltip(MSG.digitizeObjects());
+
+        button.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent e) {
+                if (button.isSelected()) {
+                    cmd.enable();
+                }
+                else {
+                    cmd.disable();
+                }
+            }
+        });
+
+        form = new DynamicForm();
+        form.setWidth(100);
+        form.setTitlePrefix("");
+        form.setTitleSuffix("");
+
+        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+        map.put(BARRIER_PIPE1, MSG.getString(BARRIER_PIPE1));
+        map.put(BARRIER_PIPE2, MSG.getString(BARRIER_PIPE2));
+        map.put(BARRIER_DITCH, MSG.getString(BARRIER_DITCH));
+        map.put(BARRIER_DAM, MSG.getString(BARRIER_DAM));
+        map.put(BARRIER_RINGDIKE, MSG.getString(BARRIER_RINGDIKE));
+
+        LinkedHashMap<String, String> ics = new LinkedHashMap<String, String>();
+        ics.put(BARRIER_PIPE1, BARRIER_PIPE1);
+        ics.put(BARRIER_PIPE2, BARRIER_PIPE2);
+        ics.put(BARRIER_DITCH, BARRIER_DITCH);
+        ics.put(BARRIER_DAM, BARRIER_DAM);
+        ics.put(BARRIER_RINGDIKE, BARRIER_RINGDIKE);
+
+        SelectItem box = new SelectItem(FIELD_BARRIER_TYPE);
+        box.setTitle("");
+        box.setWidth(100);
+        box.setValueMap(map);
+        box.setImageURLSuffix(".png");
+        box.setValueIcons(ics);
+
+        box.addChangedHandler(new ChangedHandler() {
+            public void onChanged(ChangedEvent e) {
+                setSelectedControl();
+            }
+        });
+
+        form.setFields(box);
+
+        addMember(button);
+        addMember(form);
+
+        layer.addVectorFeatureAddedListener(this);
+
+        activate(false);
+    }
+
+
+    protected String getSelectedType() {
+        return form.getValueAsString(FIELD_BARRIER_TYPE);
+    }
+
+
+    public void onFeatureAdded(FeatureAddedEvent evt) {
+        setCurrentType(evt.getVectorFeature());
+    }
+
+
+    protected void setCurrentType(VectorFeature feature) {
+        Attributes attrs = feature.getAttributes();
+        String     type  = attrs.getAttributeAsString("typ");
+
+        if (type == null || type.length() == 0) {
+            type = getSelectedType();
+
+            Style style = FloodMap.getStyle(type);
+            if (style != null) {
+                feature.setStyle(style);
+            }
+
+            if (type.equals(BARRIER_PIPE1)) {
+                attrs.setAttribute("typ", BARRIER_PIPE1_VALUE);
+            }
+            else if (type.equals(BARRIER_PIPE2)) {
+                attrs.setAttribute("typ", BARRIER_PIPE2_VALUE);
+            }
+            else if (type.equals(BARRIER_DAM)) {
+                attrs.setAttribute("typ", BARRIER_DAM_VALUE);
+            }
+            else if (type.equals(BARRIER_DITCH)) {
+                attrs.setAttribute("typ", BARRIER_DITCH_VALUE);
+            }
+            else if (type.equals(BARRIER_RINGDIKE)) {
+                attrs.setAttribute("typ", BARRIER_RINGDIKE_VALUE);
+            }
+
+            layer.redraw();
+        }
+    }
+
+
+    protected void removeControl() {
+        if (control != null) {
+            control.deactivate();
+            map.removeControl(control);
+        }
+    }
+
+
+    protected void setSelectedControl() {
+        removeControl();
+
+        String type = getSelectedType();
+
+        if (type == null || type.length() == 0) {
+            return;
+        }
+
+        if (type.equalsIgnoreCase(BARRIER_RINGDIKE)) {
+            control = createDrawPolygonControl();
+        }
+        else {
+            control = createDrawLineControl();
+        }
+
+        map.addControl(control);
+        control.activate();
+    }
+
+
+    protected Control createDrawControl(Handler handler) {
+        return new DrawFeature(layer, handler);
+    }
+
+
+    protected Control createDrawPolygonControl() {
+        return createDrawControl(new PolygonHandler());
+    }
+
+
+    protected Control createDrawLineControl() {
+        return createDrawControl(new PathHandler());
+    }
+
+
+    public void activate(boolean activate) {
+        FormItem item = form.getField(FIELD_BARRIER_TYPE);
+
+        if (activate) {
+            button.select();
+            item.enable();
+            setSelectedControl();
+        }
+        else {
+            removeControl();
+            button.deselect();
+            item.disable();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/ElevationWindow.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,432 @@
+package de.intevation.flys.client.client.ui.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.types.SelectionStyle;
+import com.smartgwt.client.types.SortArrow;
+import com.smartgwt.client.types.SortDirection;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.IButton;
+import com.smartgwt.client.widgets.Window;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.events.CloseClientEvent;
+import com.smartgwt.client.widgets.events.CloseClickHandler;
+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.EditCompleteEvent;
+import com.smartgwt.client.widgets.grid.events.EditCompleteHandler;
+import com.smartgwt.client.widgets.grid.events.RowOutEvent;
+import com.smartgwt.client.widgets.grid.events.RowOutHandler;
+import com.smartgwt.client.widgets.grid.events.RowOverEvent;
+import com.smartgwt.client.widgets.grid.events.RowOverHandler;
+
+import org.gwtopenmaps.openlayers.client.Style;
+import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
+import org.gwtopenmaps.openlayers.client.geometry.Geometry;
+import org.gwtopenmaps.openlayers.client.geometry.LinearRing;
+import org.gwtopenmaps.openlayers.client.geometry.LineString;
+import org.gwtopenmaps.openlayers.client.geometry.Point;
+import org.gwtopenmaps.openlayers.client.geometry.Polygon;
+import org.gwtopenmaps.openlayers.client.layer.Vector;
+import org.gwtopenmaps.openlayers.client.layer.VectorOptions;
+import org.gwtopenmaps.openlayers.client.util.Attributes;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+
+public class ElevationWindow extends Window {
+
+    public static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    public static final int WINDOW_WIDTH  = 250;
+    public static final int WINDOW_HEIGHT = 250;
+
+    protected FloodMap      floodmap;
+    protected Vector        layer;
+    protected VectorFeature feature;
+
+    protected ListGrid grid;
+
+
+    private class PointRecord extends ListGridRecord {
+        protected VectorFeature point;
+
+        public PointRecord(VectorFeature point, double x, double y, String z) {
+            super();
+
+            this.point = point;
+            setAttribute("x", x);
+            setAttribute("y", y);
+            setAttribute("z", z);
+        }
+
+        public VectorFeature getPoint() {
+            return point;
+        }
+    }
+
+
+    public ElevationWindow(FloodMap floodmap, VectorFeature feature) {
+        super();
+        this.feature  = feature;
+        this.floodmap = floodmap;
+
+        init();
+    }
+
+
+    protected void init() {
+        addCloseClickHandler(new CloseClickHandler() {
+            public void onCloseClick(CloseClientEvent evt) {
+                doClose();
+            }
+        });
+
+        initLayout();
+        initEdit();
+        updateGrid();
+    }
+
+
+    protected void initLayout() {
+        setWidth(WINDOW_WIDTH);
+        setHeight(WINDOW_HEIGHT);
+        setTitle(MSG.ele_window_title());
+
+        VLayout root = new VLayout();
+        root.setMembersMargin(5);
+        root.setPadding(5);
+
+        root.addMember(getLabel());
+        root.addMember(getGrid());
+        root.addMember(getButtonBar());
+
+        addItem(root);
+        centerInPage();
+    }
+
+
+    protected void initEdit() {
+        VectorOptions opts = new VectorOptions();
+        opts.setProjection(floodmap.getRiverProjection());
+        opts.setMaxExtent(floodmap.getMaxExtent());
+
+        layer = new Vector("tmp", opts);
+        layer.setIsBaseLayer(false);
+
+        floodmap.getMap().addLayer(layer);
+    }
+
+
+    public Style getStyle() {
+        Style style = new Style();
+        style.setStrokeColor("#000000");
+        style.setStrokeWidth(1);
+        style.setFillColor("#FF0000");
+        style.setFillOpacity(0.5);
+        style.setPointRadius(5);
+        style.setStrokeOpacity(1.0);
+        return style;
+    }
+
+
+    public Style getHighStyle() {
+        Style style = new Style();
+        style.setStrokeColor("#000000");
+        style.setStrokeWidth(1);
+        style.setFillColor("#FFFF22");
+        style.setFillOpacity(0.5);
+        style.setPointRadius(5);
+        style.setStrokeOpacity(1.0);
+        return style;
+    }
+
+
+    protected Label getLabel() {
+        Label label = new Label(MSG.ele_window_label());
+        label.setHeight(25);
+
+        return label;
+    }
+
+
+    protected ListGrid getGrid() {
+        if (grid == null) {
+            grid = new ListGrid();
+            grid.setCanEdit(true);
+            grid.setCanReorderFields(false);
+            grid.setAutoFitMaxWidth(WINDOW_WIDTH);
+            grid.setShowHeaderContextMenu(false);
+            grid.setShowSortArrow(SortArrow.NONE);
+            grid.setSortDirection(SortDirection.DESCENDING);
+            grid.setSelectionType(SelectionStyle.NONE);
+
+            ListGridField x = new ListGridField("x", MSG.ele_window_x_col());
+            x.setCanEdit(false);
+
+            ListGridField y = new ListGridField("y", MSG.ele_window_y_col());
+            y.setCanEdit(false);
+
+            ListGridField z = new ListGridField("z", MSG.ele_window_z_col());
+            z.setCanEdit(true);
+
+            grid.setFields(x, y, z);
+
+            grid.addRowOverHandler(new RowOverHandler() {
+                public void onRowOver(RowOverEvent evt) {
+                    PointRecord   pr = (PointRecord) evt.getRecord();
+                    VectorFeature p  = pr.getPoint();
+
+                    p.setStyle(getHighStyle());
+                    layer.redraw();
+                }
+            });
+
+            grid.addRowOutHandler(new RowOutHandler() {
+                public void onRowOut(RowOutEvent evt) {
+                    PointRecord   pr = (PointRecord) evt.getRecord();
+                    VectorFeature p  = pr.getPoint();
+
+                    p.setStyle(getStyle());
+                    layer.redraw();
+                }
+            });
+
+            grid.addEditCompleteHandler(new EditCompleteHandler() {
+                public void onEditComplete(EditCompleteEvent evt) {
+                    NumberFormat nf = NumberFormat.getDecimalFormat();
+                    String        z = (String) evt.getNewValues().get("z");
+
+                    try {
+                        nf.parse(z);
+                    }
+                    catch (NumberFormatException nfe) {
+                        SC.warn(MSG.ele_window_format_error() + " " + z);
+
+                        PointRecord old = (PointRecord) evt.getOldRecord();
+
+                        ListGridRecord[] records = grid.getRecords();
+                        records[evt.getRowNum()] = old;
+
+                        grid.setRecords(records);
+                    }
+
+                }
+            });
+        }
+
+        return grid;
+    }
+
+
+    protected HLayout getButtonBar() {
+        HLayout bar = new HLayout();
+        bar.setAlign(Alignment.CENTER);
+        bar.setHeight(25);
+        bar.setMembersMargin(15);
+
+        bar.addMember(getOKButton());
+        bar.addMember(getCancelButton());
+
+        return bar;
+    }
+
+
+    protected IButton getOKButton() {
+        IButton btn = new IButton(MSG.ele_window_ok_button());
+
+        btn.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent evt) {
+                if (saveElevation()) {
+                    doClose();
+                }
+
+            }
+        });
+
+        return btn;
+    }
+
+
+    protected IButton getCancelButton() {
+        IButton btn = new IButton(MSG.ele_window_cancel_button());
+
+        btn.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent evt) {
+                doClose();
+            }
+        });
+
+        return btn;
+    }
+
+
+    public void updateGrid() {
+        Attributes attr = feature.getAttributes();
+        Geometry   geom = feature.getGeometry();
+
+        String barrierType = null;
+
+        if (attr != null) {
+            barrierType = attr.getAttributeAsString("typ");
+        }
+        else {
+            barrierType = DrawControl.BARRIER_DAM_VALUE;
+        }
+
+        GWT.log("Geometry is from type: " + geom.getClassName());
+        GWT.log("Barrier  is from type: " + barrierType);
+
+        double[][] points = getPointsFromGeometry(geom);
+        double[]   ele    = extractElevations(attr);
+
+        if (points == null) {
+            return;
+        }
+
+        NumberFormat nf = NumberFormat.getDecimalFormat();
+
+        ListGrid grid = getGrid();
+
+        int zLen = ele != null ? ele.length : 0;
+
+        for (int i = 0, len = points.length; i < len; i++) {
+            double[]  point = points[i];
+            VectorFeature p = addPoint(point[0], point[1]);
+
+            String value = null;
+
+            if (zLen > i) {
+                value = nf.format(ele[i]);
+            }
+            else if (barrierType.equals(DrawControl.BARRIER_DITCH_VALUE)) {
+                value = "-9999";
+            }
+            else {
+                value = "9999";
+            }
+
+            grid.addData(new PointRecord(p, point[0], point[1], value));
+        }
+
+        grid.redraw();
+    }
+
+
+    public static double[] extractElevations(Attributes attr) {
+        if (attr == null) {
+            return null;
+        }
+
+        String   elevationStr = attr.getAttributeAsString("elevation");
+
+        if (elevationStr == null || elevationStr.length() == 0) {
+            return null;
+        }
+
+        String[] elevations   = elevationStr.split(" ");
+
+        int len = elevations != null ? elevations.length : 0;
+
+        if (len == 0) {
+            return null;
+        }
+
+        double[] res = new double[len];
+
+        for (int i = 0; i < len; i++) {
+            try {
+                res[i] = Double.valueOf(elevations[i]);
+            }
+            catch (NumberFormatException nfe) {
+                // go on
+            }
+        }
+
+        return res;
+    }
+
+
+    public static double[][] getPointsFromGeometry(Geometry geom) {
+        String clazz = geom.getClassName();
+
+        if (clazz != null && clazz.equals(Geometry.LINESTRING_CLASS_NAME)) {
+            return getPointsFromLineString(
+                LineString.narrowToLineString(geom.getJSObject()));
+        }
+        else if (clazz != null && clazz.equals(Geometry.POLYGON_CLASS_NAME)) {
+            return getPointsFromPolygon(
+                Polygon.narrowToPolygon(geom.getJSObject()));
+        }
+        else {
+            SC.warn(MSG.ele_window_geometry_error() + " " + clazz);
+        }
+
+        return null;
+    }
+
+
+    public static double[][] getPointsFromLineString(LineString line) {
+        return line.getCoordinateArray();
+    }
+
+
+    public static double[][] getPointsFromPolygon(Polygon polygon) {
+        LinearRing[] rings = polygon.getComponents();
+
+        return getPointsFromLineString(rings[0]);
+    }
+
+
+    protected VectorFeature addPoint(double x, double y) {
+        VectorFeature point = new VectorFeature(new Point(x, y), getStyle());
+        layer.addFeature(point);
+
+        return point;
+    }
+
+
+    protected boolean saveElevation() {
+        ListGridRecord[] records = grid.getRecords();
+
+        NumberFormat nf = NumberFormat.getDecimalFormat();
+
+        StringBuilder sb = new StringBuilder();
+
+        for (ListGridRecord record: records) {
+            PointRecord pr = (PointRecord) record;
+            String value   = pr.getAttributeAsString("z");
+
+            try {
+                double z = nf.parse(value);
+                sb.append(String.valueOf(z));
+            }
+            catch (NumberFormatException nfe) {
+                SC.warn(MSG.ele_window_save_error());
+                return false;
+            }
+
+            sb.append(" ");
+        }
+
+        Attributes attr = feature.getAttributes();
+        attr.setAttribute("elevation", sb.toString());
+
+        return true;
+    }
+
+
+    protected void doClose() {
+        floodmap.getMap().removeLayer(layer);
+        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/map/FloodMap.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,268 @@
+package de.intevation.flys.client.client.ui.map;
+
+import org.gwtopenmaps.openlayers.client.Bounds;
+import org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.MapOptions;
+import org.gwtopenmaps.openlayers.client.MapWidget;
+import org.gwtopenmaps.openlayers.client.Style;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener.FeatureAddedEvent;
+import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
+import org.gwtopenmaps.openlayers.client.format.GeoJSON;
+import org.gwtopenmaps.openlayers.client.layer.Vector;
+import org.gwtopenmaps.openlayers.client.layer.VectorOptions;
+import org.gwtopenmaps.openlayers.client.util.Attributes;
+import org.gwtopenmaps.openlayers.client.util.JObjectArray;
+import org.gwtopenmaps.openlayers.client.util.JSObject;
+
+
+public class FloodMap implements VectorFeatureAddedListener {
+
+    public static final String LAYER_BARRIERS = "vector_layer_barriers";
+
+    public static final String MARK_SELECTED = "mark.selected";
+
+    public static final int SELECTED_STROKE_WIDTH = 2;
+
+    protected MapWidget mapWidget;
+    protected Map       map;
+    protected Vector    barrierLayer;
+    protected String    srid;
+    protected Bounds    maxExtent;
+
+
+    public FloodMap(String srid, Bounds maxExtent) {
+        this.srid      = srid;
+        this.maxExtent = maxExtent;
+
+        MapOptions opts = new MapOptions();
+        opts.setControls(new JObjectArray(new JSObject[] {}));
+        opts.setNumZoomLevels(16);
+        opts.setProjection(getRiverProjection());
+        opts.setMaxExtent(maxExtent);
+        opts.setUnits("m");
+        opts.setMaxResolution(500); // TODO DO THIS ON THE FLY
+
+        mapWidget = new MapWidget("510px", "635px", opts);
+        map       = mapWidget.getMap();
+
+        getBarrierLayer();
+    }
+
+
+    @Override
+    public void onFeatureAdded(FeatureAddedEvent evt) {
+        VectorFeature feature = evt.getVectorFeature();
+
+        Attributes attrs = feature.getAttributes();
+        String     type  = attrs.getAttributeAsString("typ");
+
+        if (type == null || type.length() == 0) {
+            return;
+        }
+
+        Style style = getStyle(type);
+        if (style != null) {
+            feature.setStyle(style);
+        }
+
+        // necessary, otherwise the setStyle() has no effect
+        barrierLayer.redraw();
+    }
+
+
+    /**
+     * Returns an OpenLayers.Style based on a given type.
+     *
+     * @param type Type can be one of "pipe1", "pipe2", "ditch", "dam",
+     * "ringdike".
+     *
+     * @return an OpenLayers.Style object.
+     */
+    public static Style getStyle(String type) {
+        Style style = new Style();
+
+        if (type == null) {
+            return null;
+        }
+
+        if (type.equals(DrawControl.BARRIER_PIPE1)
+            || type.equals(DrawControl.BARRIER_PIPE1_VALUE)
+        ) {
+            style.setFillColor("#800080");
+            style.setStrokeColor("#800080");
+        }
+        else if (type.equals(DrawControl.BARRIER_PIPE2)
+              || type.equals(DrawControl.BARRIER_PIPE2_VALUE)
+        ) {
+            style.setFillColor("#808080");
+            style.setStrokeColor("#808080");
+        }
+        else if (type.equals(DrawControl.BARRIER_DAM)
+              || type.equals(DrawControl.BARRIER_DAM_VALUE)
+        ) {
+            style.setFillColor("#008000");
+            style.setStrokeColor("#008000");
+        }
+        else if (type.equals(DrawControl.BARRIER_DITCH)
+              || type.equals(DrawControl.BARRIER_DITCH_VALUE)
+        ) {
+            style.setFillColor("#800000");
+            style.setStrokeColor("#800000");
+        }
+        else if (type.equals(DrawControl.BARRIER_RINGDIKE)
+              || type.equals(DrawControl.BARRIER_RINGDIKE_VALUE)
+        ) {
+            style.setFill(false);
+            style.setStrokeColor("#FF8000");
+        }
+
+        return style;
+    }
+
+
+    public MapWidget getMapWidget() {
+        return mapWidget;
+    }
+
+
+    public Map getMap() {
+        return map;
+    }
+
+
+    public String getRiverProjection() {
+        return "EPSG:" + srid;
+    }
+
+
+    public Bounds getMaxExtent() {
+        return maxExtent;
+    }
+
+
+    public Vector getBarrierLayer() {
+        if (barrierLayer == null) {
+            VectorOptions opts = new VectorOptions();
+            opts.setProjection(getRiverProjection());
+            opts.setMaxExtent(getMaxExtent());
+
+            barrierLayer = new Vector(LAYER_BARRIERS, opts);
+            barrierLayer.setIsBaseLayer(true);
+
+            map.addLayer(barrierLayer);
+
+            barrierLayer.addVectorFeatureAddedListener(this);
+        }
+
+        return barrierLayer;
+    }
+
+
+    public String getFeaturesAsGeoJSON() {
+        // disable features before exporting to GeoJSON
+        disableFeatures();
+
+        VectorFeature[] features = barrierLayer.getFeatures();
+
+        if (features == null || features.length == 0) {
+            return null;
+        }
+
+        return new GeoJSON().write(features);
+    }
+
+
+    public void setSize(String width, String height) {
+        mapWidget.setWidth(width);
+        mapWidget.setHeight(height);
+    }
+
+
+    public void hideBarrierLayer () {
+        if (barrierLayer != null) {
+            barrierLayer.setIsVisible(false);
+        }
+    }
+
+    public void showBarrierLayer () {
+        if (barrierLayer != null) {
+            barrierLayer.setIsVisible(true);
+        }
+    }
+
+
+    public void selectFeature(VectorFeature feature) {
+        if (feature != null) {
+            selectFeatures(new VectorFeature[] { feature } );
+        }
+    }
+
+
+    public void selectFeatures(VectorFeature[] features) {
+        if (features == null || features.length == 0) {
+            return;
+        }
+
+        for (VectorFeature feature: features) {
+            Attributes attr = feature.getAttributes();
+
+            if (attr.getAttributeAsInt(MARK_SELECTED) == 1) {
+                continue;
+            }
+
+            attr.setAttribute(MARK_SELECTED, 1);
+
+            Style style        = feature.getStyle();
+            double strokeWidth = style.getStrokeWidth();
+
+            style.setStrokeWidth(strokeWidth+SELECTED_STROKE_WIDTH);
+        }
+
+        getBarrierLayer().redraw();
+    }
+
+
+    public void disableFeatures() {
+        Vector          barriers = getBarrierLayer();
+        VectorFeature[] features = barriers.getFeatures();
+
+        if (features == null || features.length == 0) {
+            return;
+        }
+
+        disableFeatures(features);
+    }
+
+
+    public void disableFeature(VectorFeature feature) {
+        if (feature != null) {
+            disableFeatures(new VectorFeature[] { feature });
+        }
+    }
+
+
+    public void disableFeatures(VectorFeature[] features) {
+        if (features == null || features.length == 0) {
+            return;
+        }
+
+        for (VectorFeature feature: features) {
+            Attributes attr = feature.getAttributes();
+
+            if (attr.getAttributeAsInt(MARK_SELECTED) == 0) {
+                continue;
+            }
+
+            attr.setAttribute(FloodMap.MARK_SELECTED, 0);
+
+            Style style        = feature.getStyle();
+            double strokeWidth = style.getStrokeWidth();
+
+            style.setStrokeWidth(strokeWidth-SELECTED_STROKE_WIDTH);
+        }
+
+        getBarrierLayer().redraw();
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/MapOutputTab.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,483 @@
+package de.intevation.flys.client.client.ui.map;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.events.ResizedEvent;
+import com.smartgwt.client.widgets.events.ResizedHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import org.gwtopenmaps.openlayers.client.Bounds;
+import org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener.FeatureAddedEvent;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureRemovedListener;
+import org.gwtopenmaps.openlayers.client.event.VectorFeatureRemovedListener.FeatureRemovedEvent;
+import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
+import org.gwtopenmaps.openlayers.client.format.GeoJSON;
+import org.gwtopenmaps.openlayers.client.layer.Layer;
+import org.gwtopenmaps.openlayers.client.layer.Vector;
+import org.gwtopenmaps.openlayers.client.layer.WMS;
+import org.gwtopenmaps.openlayers.client.layer.WMSParams;
+import org.gwtopenmaps.openlayers.client.layer.WMSOptions;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import de.intevation.flys.client.shared.model.AttributedTheme;
+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.MapConfig;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.ThemeList;
+import de.intevation.flys.client.shared.model.OutputMode;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.services.StepForwardService;
+import de.intevation.flys.client.client.services.StepForwardServiceAsync;
+import de.intevation.flys.client.client.services.MapOutputService;
+import de.intevation.flys.client.client.services.MapOutputServiceAsync;
+import de.intevation.flys.client.client.event.RedrawRequestHandler;
+import de.intevation.flys.client.client.event.RedrawRequestEvent;
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.OutputTab;
+import de.intevation.flys.client.client.ui.ThemePanel;
+
+
+public class MapOutputTab extends OutputTab implements RedrawRequestHandler {
+
+    public static final String DEFAULT_SRID = "4326";
+
+    public static final String BARRIERS_PARAMETER_KEY = "uesk.barriers";
+
+    public static final String WSPLGEN_FACET = "floodmap.wsplgen";
+
+
+    protected StepForwardServiceAsync feedService =
+        GWT.create(StepForwardService.class);
+
+    protected MapOutputServiceAsync mapService =
+        GWT.create(MapOutputService.class);
+
+    protected MapToolbar controlPanel;
+    protected ThemePanel themePanel;
+    protected Widget     mapPanel;
+
+    protected FloodMap floodMap;
+
+
+    public MapOutputTab(
+        String         title,
+        Collection     collection,
+        OutputMode     mode,
+        CollectionView collectionView
+    ){
+        super(title, collection, collectionView, mode);
+
+        mapService.doOut(collection, new AsyncCallback<MapConfig>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("MAP ERROR: " + caught.getMessage());
+                }
+
+                public void onSuccess(MapConfig c) {
+                    GWT.log("MAP SUCCESS!");
+
+                    Bounds max     = boundsFromString(c.getMaxExtent());
+                    Bounds initial = boundsFromString(c.getInitialExtent());
+
+                    if (initial == null) {
+                        GWT.log("Warning: No initial extent set.");
+                        initial = max;
+                    }
+
+                    setFloodmap(new FloodMap(c.getSrid(), max));
+
+                    initLayout();
+                    initBarriers();
+
+                    GWT.log("MAX EXTENT: " + max);
+                    GWT.log("ZOOM TO: " + initial);
+                    getMap().zoomToExtent(initial);
+                }
+            }
+        );
+    }
+
+
+    protected void initLayout() {
+        VLayout rootLayout = new VLayout();
+        rootLayout.setHeight100();
+        rootLayout.setWidth100();
+        rootLayout.setMembersMargin(2);
+
+        final Canvas  wrapper = new Canvas();
+        wrapper.setWidth100();
+        wrapper.setHeight100();
+
+        controlPanel = createControlPanel(wrapper);
+        mapPanel     = floodMap.getMapWidget();
+        Canvas themePanelWrapper = createThemePanel();
+
+        final HorizontalPanel layout = new HorizontalPanel();
+        layout.setWidth("99%");
+        layout.setHeight("99%");
+        layout.add(mapPanel);
+
+        wrapper.addChild(layout);
+
+        HLayout hlayout = new HLayout();
+        hlayout.setHeight("*");
+        hlayout.setWidth100();
+        hlayout.setMembersMargin(2);
+
+        hlayout.addMember(themePanelWrapper);
+        hlayout.addMember(wrapper);
+
+        rootLayout.addMember(controlPanel);
+        rootLayout.addMember(hlayout);
+
+        wrapper.addResizedHandler(new ResizedHandler() {
+            public void onResized(ResizedEvent e) {
+                Integer height = wrapper.getHeight();
+                Integer width  = wrapper.getWidth();
+
+                height = height * 99 / 100;
+                width  = width  * 99 / 100;
+
+                String w = String.valueOf(width) + "px";
+                String h = String.valueOf(height) + "px";
+
+                floodMap.setSize(w, h);
+            }
+        });
+
+        setPane(rootLayout);
+    }
+
+
+    protected void initBarriers() {
+        Vector vector = floodMap.getBarrierLayer();
+        vector.addVectorFeatureAddedListener(
+            new VectorFeatureAddedListener() {
+                public void onFeatureAdded(FeatureAddedEvent e) {
+                    saveBarriers();
+                }
+            }
+        );
+
+        vector.addVectorFeatureRemovedListener(
+            new VectorFeatureRemovedListener() {
+                public void onFeatureRemoved(FeatureRemovedEvent e) {
+                    saveBarriers();
+                }
+            }
+        );
+
+
+        Artifact artifact = getArtifact();
+
+        if (artifact == null) {
+            return;
+        }
+
+        ArtifactDescription desc = artifact.getArtifactDescription();
+
+        String geojson = getGeoJSONFromStatic(desc);
+        geojson        = geojson != null ? geojson : getGeoJSONFromDynamic(desc);
+
+        if (geojson == null || geojson.length() == 0) {
+            GWT.log("No geojson string found -> no barriers existing.");
+            return;
+        }
+
+        GeoJSON reader = new GeoJSON();
+        VectorFeature[] features = reader.read(geojson);
+
+        vector.addFeatures(features);
+    }
+
+
+    public void addLayer(Layer layer) {
+        Map map = getMap();
+
+        if (map != null) {
+            GWT.log("Add new layer '" + layer.getName() + "' to map.");
+            map.addLayer(layer);
+        }
+    }
+
+
+    public void removeLayer(String name) {
+        Map map = getMap();
+
+        Layer[] layers = map.getLayers();
+
+        for (Layer layer: layers) {
+            if (name.equals(layer.getName())) {
+                map.removeLayer(layer);
+            }
+        }
+    }
+
+
+    @Override
+    public void onRedrawRequest(RedrawRequestEvent event) {
+        mapService.doOut(collection, new AsyncCallback<MapConfig>() {
+            public void onFailure(Throwable caught) {
+                GWT.log("MAP ERROR: " + caught.getMessage());
+            }
+
+            public void onSuccess(MapConfig c) {
+                themePanel.updateCollection();
+            }
+        });
+    }
+
+
+    protected void setFloodmap(FloodMap floodMap) {
+        this.floodMap = floodMap;
+    }
+
+
+    protected Map getMap() {
+        return floodMap.getMap();
+    }
+
+
+    protected ThemePanel getThemePanel() {
+        return themePanel;
+    }
+
+
+    protected String getGeoJSONFromDynamic(ArtifactDescription desc) {
+        DataList list = desc.getCurrentData();
+
+        if (list == null) {
+            return null;
+        }
+
+        List<Data> datas = list.getAll();
+        for (Data data: datas) {
+            String key = data.getLabel();
+
+            if (key != null && key.equals(BARRIERS_PARAMETER_KEY)) {
+                DataItem def = data.getDefault();
+
+                if (def != null) {
+                    return def.getStringValue();
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    protected String getGeoJSONFromStatic(ArtifactDescription desc) {
+        // TODO Implement this method, if there are reachable states right after
+        // the floodmap state - which is currently not the case.
+        return null;
+    }
+
+
+    public ThemeList getThemeList() {
+        return collection.getThemeList(mode.getName());
+    }
+
+
+    public String getSrid() {
+        ThemeList themeList = getThemeList();
+
+        int num = themeList.getThemeCount();
+
+        for (int i = 1; i <= num; i++) {
+            AttributedTheme theme = (AttributedTheme) themeList.getThemeAt(i);
+
+            if (theme == null) {
+                continue;
+            }
+
+            String srid = theme.getAttr("srid");
+
+            if (srid != null && srid.length() > 0) {
+                return srid;
+            }
+        }
+
+        return DEFAULT_SRID;
+    }
+
+
+    protected Bounds boundsFromString(String bounds) {
+        GWT.log("Create Bounds from String: '" + bounds + "'");
+        if (bounds == null || bounds.length() == 0) {
+            return null;
+        }
+
+        String[] values = bounds.split(" ");
+
+        if (values == null || values.length < 4) {
+            return null;
+        }
+
+        try {
+            return new Bounds(
+                Double.valueOf(values[0]),
+                Double.valueOf(values[1]),
+                Double.valueOf(values[2]),
+                Double.valueOf(values[3]));
+        }
+        catch (NumberFormatException nfe) {}
+
+        return null;
+    }
+
+
+    public Layer createWMSLayer(Theme theme) {
+        if (theme == null) {
+            return null;
+        }
+
+        AttributedTheme at = (AttributedTheme) theme;
+
+        String type   = at.getAttr("name");
+        String desc   = at.getAttr("description");
+        String url    = at.getAttr("url");
+        String layers = at.getAttr("layers");
+
+        if (url == null || layers == null) {
+            return null;
+        }
+
+        //boolean baseLayer = type.equals("floodmap.wmsbackground");
+
+        WMSParams params = new WMSParams();
+        params.setLayers(layers);
+        params.setFormat("image/png");
+        params.setIsTransparent(true);
+
+        WMSOptions opts = new WMSOptions();
+        opts.setProjection("EPSG:" + getSrid());
+        opts.setSingleTile(true);
+        opts.setRatio(1);
+
+        WMS wms = new WMS(desc, url, params, opts);
+        wms.setIsVisible(at.getActive() == 1);
+        wms.setIsBaseLayer(false);
+
+        return wms;
+    }
+
+
+    protected MapToolbar createControlPanel(Canvas wrapper) {
+        return new MapToolbar(this, floodMap, wrapper, false);
+    }
+
+
+    protected Canvas createThemePanel() {
+        Canvas c = new Canvas();
+        c.setMinWidth(300);
+        c.setWidth(300);
+        c.setHeight100();
+        c.setCanDragResize(true);
+        c.setBorder("1px solid black");
+
+        themePanel = new MapThemePanel(
+            collection,
+            mode,
+            this,
+            new MapThemePanel.ActivateCallback() {
+                public void activate(Theme theme, boolean active) {
+                    activateTheme(theme, active);
+                }
+            },
+            new MapThemePanel.ThemeMovedCallback() {
+                public void onThemeMoved(Theme theme, int oldIdx, int newIdx) {
+                    // this code synchronizes the ThemePanel and the OpenLayers
+                    // internal order of layers.
+                    String    name = theme.getDescription();
+                    Map        map = getMap();
+                    Layer[] layers = map.getLayersByName(name);
+
+                    if (layers == null || layers.length == 0) {
+                        GWT.log("Error: Cannot find layer '" + name + "'");
+                        return;
+                    }
+
+                    map.raiseLayer(layers[0], (newIdx-oldIdx)*-1);
+                    map.zoomTo(map.getZoom()-1);
+                    map.zoomTo(map.getZoom()+1);
+                }
+            },
+            new MapThemePanel.LayerZoomCallback() {
+                public void onLayerZoom(Theme theme, String extent) {
+                    Bounds zoomTo = boundsFromString(extent);
+
+                    if (zoomTo == null) {
+                        GWT.log("WARNING: No valid bounds for zooming found!");
+                        return;
+                    }
+
+                    getMap().zoomToExtent(zoomTo);
+                }
+            }
+        );
+        c.addChild(themePanel);
+
+        return c;
+    }
+
+
+    protected void activateTheme(Theme theme, boolean active) {
+        String name = theme.getDescription();
+        Layer layer = floodMap.getMap().getLayerByName(name);
+
+        GWT.log("Set visibility of '" + name + "': " + active);
+
+        if (layer != null) {
+            layer.setIsVisible(active);
+        }
+    }
+
+
+    protected void saveBarriers() {
+        Vector layer = floodMap.getBarrierLayer();
+
+        GeoJSON format   = new GeoJSON();
+        String  features = format.write(layer.getFeatures());
+
+        DataItem item = new DefaultDataItem(
+            BARRIERS_PARAMETER_KEY, BARRIERS_PARAMETER_KEY, features);
+
+        Data data = new DefaultData(
+            BARRIERS_PARAMETER_KEY, BARRIERS_PARAMETER_KEY, "String",
+            new DataItem[] {item} );
+
+        Config config       = Config.getInstance();
+        final String url    = config.getServerUrl();
+        final String locale = config.getLocale();
+
+        feedService.go(url, locale, getArtifact(), new Data[] { data },
+            new AsyncCallback<Artifact>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not save barrier geometries: " +
+                        caught.getMessage());
+                }
+
+                public void onSuccess(Artifact artifact) {
+                    GWT.log("Successfully saved barrier geometries.");
+                }
+            }
+        );
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/MapPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,66 @@
+package de.intevation.flys.client.client.ui.map;
+
+import com.google.gwt.user.client.ui.HorizontalPanel;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import org.gwtopenmaps.openlayers.client.Bounds;
+
+import de.intevation.flys.client.shared.model.BBox;
+import de.intevation.flys.client.shared.model.MapInfo;
+
+
+public class MapPanel extends VLayout {
+
+    protected MapToolbar      toolbar;
+    protected HorizontalPanel mapArea;
+
+    protected FloodMap floodMap;
+    protected boolean  digitizeEnabled;
+
+
+    public MapPanel(MapInfo mapInfo, boolean digitizeEnabled) {
+        super();
+
+        BBox bbox = mapInfo.getBBox();
+
+        this.digitizeEnabled = digitizeEnabled;
+        this.floodMap        = new FloodMap(
+            String.valueOf(mapInfo.getSrid()),
+            new Bounds(
+                bbox.getLowerX(),
+                bbox.getLowerY(),
+                bbox.getUpperX(),
+                bbox.getUpperY()));
+
+        initLayout();
+    }
+
+
+    private void initLayout() {
+        setWidth100();
+        setHeight100();
+
+        mapArea = new HorizontalPanel();
+        mapArea.setWidth("99%");
+        mapArea.setHeight("99%");
+        mapArea.add(floodMap.getMapWidget());
+
+        Canvas wrapper = new Canvas();
+        wrapper.setWidth100();
+        wrapper.setHeight100();
+        wrapper.addChild(mapArea);
+
+        toolbar = new MapToolbar(floodMap, this, digitizeEnabled);
+
+        addMember(toolbar);
+        addMember(mapArea);
+    }
+
+
+    public FloodMap getFloodMap() {
+        return floodMap;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/MapPositionPanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,70 @@
+package de.intevation.flys.client.client.ui.map;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.events.MouseMoveEvent;
+import com.smartgwt.client.widgets.events.MouseMoveHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import org.gwtopenmaps.openlayers.client.LonLat;
+import org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.MapWidget;
+import org.gwtopenmaps.openlayers.client.Pixel;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapPositionPanel extends HLayout {
+
+    protected MapWidget mapWidget;
+    protected Map       map;
+
+    protected Label  x;
+    protected Label  y;
+
+
+    public MapPositionPanel(MapWidget mapWidget, final Canvas c) {
+        this.mapWidget = mapWidget;
+        this.map       = mapWidget.getMap();
+
+        this.x  = new Label();
+        this.y  = new Label();
+        Label d = new Label("|");
+
+        setWidth(60);
+        setMembersMargin(2);
+
+        x.setWidth(25);
+        y.setWidth(25);
+        d.setWidth(5);
+
+        addMember(x);
+        addMember(d);
+        addMember(y);
+
+        // TODO We should use the MousePosition control provided by
+        // GwtOpenLayers here instead of calculating the mouse pos manually.
+        c.addMouseMoveHandler(new MouseMoveHandler() {
+            public void onMouseMove(MouseMoveEvent e) {
+                int x = e.getX() - c.getPageLeft();
+                int y = e.getY() - c.getPageTop();
+
+                LonLat point = map.getLonLatFromPixel(new Pixel(x, y));
+                setX(point.lon());
+                setY(point.lat());
+            }
+        });
+    }
+
+
+    protected void setX(double x) {
+        this.x.setContents(String.valueOf(x));
+    }
+
+
+    protected void setY(double y) {
+        this.y.setContents(String.valueOf(y));
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/MapThemePanel.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,308 @@
+package de.intevation.flys.client.client.ui.map;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.ImageStyle;
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+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.HLayout;
+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.ClickHandler;
+import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;
+
+import de.intevation.flys.client.shared.MapUtils;
+import de.intevation.flys.client.shared.model.AttributedTheme;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.FacetRecord;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.OutputMode;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.ui.ThemePanel;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapThemePanel extends ThemePanel {
+
+    public static final int CELL_HEIGHT      = 75;
+    public static final int STYLE_CELL_WIDTH = 125;
+
+
+    public interface ActivateCallback {
+        void activate(Theme theme, boolean activate);
+    }
+
+
+    public interface ThemeMovedCallback {
+        void onThemeMoved(Theme theme, int oldIdx, int newIdx);
+    }
+
+    public interface LayerZoomCallback {
+        void onLayerZoom(Theme theme, String extent);
+    }
+
+
+
+    private FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+
+    protected ActivateCallback   activateCallback;
+    protected ThemeMovedCallback themeMovedCallback;
+    protected LayerZoomCallback  layerZoomCallback;
+
+    protected ListGridRecord[] oldRecords;
+
+
+    public static final String GRID_FIELD_ACTIVE = "active";
+    public static final String GRID_FIELD_STYLE  = "style";
+    public static final String GRID_FIELD_NAME   = "name";
+
+
+    protected MapOutputTab mapOut;
+
+
+    public MapThemePanel(
+        Collection         collection,
+        MapOutputTab       mapOut,
+        OutputMode         mode,
+        ActivateCallback   activateCallback
+    ) {
+        this(collection, mode, mapOut, activateCallback, null, null);
+    }
+
+
+    public MapThemePanel(
+        Collection         collection,
+        OutputMode         mode,
+        MapOutputTab       mapOut,
+        ActivateCallback   activateCallback,
+        ThemeMovedCallback themeMovedCallback,
+        LayerZoomCallback  layerZoomCallback
+    ) {
+        super(collection, mode);
+
+        this.mapOut             = mapOut;
+        this.activateCallback   = activateCallback;
+        this.themeMovedCallback = themeMovedCallback;
+        this.layerZoomCallback  = layerZoomCallback;
+
+        initGrid();
+        initLayout();
+
+        updateGrid();
+    }
+
+
+    protected void initLayout() {
+        setWidth100();
+        setHeight100();
+
+        VLayout layout = new VLayout();
+        layout.setWidth100();
+        layout.setHeight100();
+
+        layout.addMember(list);
+        layout.addMember(navigation);
+
+        addChild(layout);
+    }
+
+
+    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.setCellHeight(CELL_HEIGHT);
+        list.setShowRecordComponents(true);
+        list.setShowRecordComponentsByCell(true);
+        list.setShowAllRecords(true);
+
+        list.addEditCompleteHandler(this);
+
+        ListGridField active = new ListGridField(GRID_FIELD_ACTIVE, " ", 20);
+        active.setType(ListGridFieldType.BOOLEAN);
+        active.setCanDragResize(false);
+
+        ListGridField style = new ListGridField(
+            GRID_FIELD_STYLE,
+            MSG.map_themepanel_header_style(),
+            STYLE_CELL_WIDTH);
+        style.setCanEdit(false);
+        style.setCanDragResize(false);
+
+        ListGridField name = new ListGridField(
+            GRID_FIELD_NAME, MSG.chart_themepanel_header_themes());
+        name.setType(ListGridFieldType.TEXT);
+
+        list.setFields(active, style, name);
+    }
+
+
+    @Override
+    protected ListGrid createNewGrid() {
+        ListGrid grid = new ListGrid() {
+            @Override
+            protected Canvas createRecordComponent(final ListGridRecord record, Integer colNum) {
+                String fieldname = getFieldName(colNum);
+
+                if (fieldname.equals(GRID_FIELD_STYLE)) {
+                    FacetRecord      r = (FacetRecord) record;
+                    AttributedTheme at = (AttributedTheme) r.getTheme();
+
+                    String imgUrl = MapUtils.getLegendGraphicUrl(
+                        at.getAttr("url"),
+                        at.getAttr("layers"));
+
+                    HLayout layout = new HLayout();
+                    layout.setAlign(VerticalAlignment.CENTER);
+                    layout.setLayoutAlign(VerticalAlignment.CENTER);
+
+                    Img img = new Img(imgUrl);
+                    img.setImageType(ImageStyle.CENTER);
+
+                    layout.addMember(img);
+
+                    return layout;
+                }
+
+                return super.createRecordComponent(record, colNum);
+            }
+        };
+
+        return grid;
+    }
+
+
+    @Override
+    protected void clearGrid() {
+        oldRecords = list.getRecords();
+        super.clearGrid();
+    }
+
+
+    @Override
+    protected void addFacetRecord(FacetRecord rec) {
+        Theme newTheme = rec.getTheme();
+        boolean  isNew = true;
+
+        for (ListGridRecord old: getOldRecords()) {
+            FacetRecord fr = (FacetRecord) old;
+
+            if (newTheme.equals(fr.getTheme())) {
+                isNew = false;
+                break;
+            }
+        }
+
+        if (isNew && mapOut != null) {
+            mapOut.addLayer(mapOut.createWMSLayer(newTheme));
+        }
+
+        super.addFacetRecord(rec);
+    }
+
+
+    @Override
+    protected Menu getSingleContextMenu(final ListGridRecord[] records) {
+        Menu menu = super.getSingleContextMenu(records);
+
+        MenuItem layerZoom = createLayerZoomItem(records);
+        if (layerZoom != null) {
+            menu.addItem(layerZoom);
+        }
+
+        return menu;
+    }
+
+
+    @Override
+    protected MenuItem createRemoveItem(final ListGridRecord[] records) {
+        MenuItem item = super.createRemoveItem(records);
+        item.addClickHandler(new ClickHandler() {
+            @Override
+            public void onClick(MenuItemClickEvent evt) {
+                for (ListGridRecord record: records) {
+                    FacetRecord facet = (FacetRecord) record;
+
+                    Theme theme = facet.getTheme();
+                    theme.setVisible(0);
+                    theme.setActive(0);
+
+                    getMapOutputTab().removeLayer(theme.getDescription());
+                }
+
+                updateCollection();
+            }
+        });
+
+        return item;
+    }
+
+
+    protected MenuItem createLayerZoomItem(final ListGridRecord[] recs) {
+        final FacetRecord     fr = (FacetRecord) recs[0];
+        final AttributedTheme at = (AttributedTheme) fr.getTheme();
+
+        final String extent = at.getAttr("extent");
+
+        if (extent == null || extent.length() == 0) {
+            return null;
+        }
+
+        MenuItem zoom = new MenuItem(MSG.zoomToLayer());
+        zoom.addClickHandler(new ClickHandler() {
+            @Override
+            public void onClick(MenuItemClickEvent evt) {
+                if (layerZoomCallback != null) {
+                    layerZoomCallback.onLayerZoom(at, extent);
+                }
+            }
+        });
+
+        return zoom;
+    }
+
+
+    @Override
+    public void activateTheme(Theme theme, boolean active) {
+        if (activateCallback != null) {
+            activateCallback.activate(theme, active);
+        }
+
+        theme.setActive(active ? 1 : 0);
+    }
+
+
+    @Override
+    protected void fireThemeMoved(Theme theme, int oldIdx, int newIdx) {
+        if (themeMovedCallback != null) {
+            themeMovedCallback.onThemeMoved(theme, oldIdx, newIdx);
+        }
+    }
+
+
+    protected ListGridRecord[] getOldRecords() {
+        return oldRecords != null ? oldRecords : new ListGridRecord[0];
+    }
+
+
+    protected MapOutputTab getMapOutputTab() {
+        return mapOut;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/MapToolbar.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,477 @@
+package de.intevation.flys.client.client.ui.map;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.SelectionType;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Button;
+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 org.gwtopenmaps.openlayers.client.Map;
+import org.gwtopenmaps.openlayers.client.control.DragPan;
+import org.gwtopenmaps.openlayers.client.control.SelectFeature;
+import org.gwtopenmaps.openlayers.client.control.SelectFeatureOptions;
+import org.gwtopenmaps.openlayers.client.control.ZoomBox;
+import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
+import org.gwtopenmaps.openlayers.client.layer.Vector;
+import org.gwtopenmaps.openlayers.client.util.Attributes;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.ui.Toolbar;
+import de.intevation.flys.client.client.utils.EnableDisableCmd;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapToolbar extends Toolbar {
+
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected FloodMap      floodMap;
+    protected DragPan       pan;
+    protected ZoomBox       zoomBox;
+    protected SelectFeature selectFeature;
+
+    protected Button datacageButton;
+
+    protected ImgButton zoomToMaxButton;
+    protected ImgButton zoomBoxButton;
+    protected ImgButton zoomOutButton;
+    protected ImgButton panButton;
+    protected ImgButton selectButton;
+    protected ImgButton removeButton;
+    protected ImgButton elevationButton;
+
+    protected DrawControl    drawControl;
+    protected MeasureControl measureControl;
+
+    protected Canvas position;
+
+
+    public MapToolbar(MapOutputTab mapTab, FloodMap floodMap, Canvas wrapper) {
+        this(mapTab, floodMap, wrapper, true);
+    }
+
+
+    public MapToolbar(FloodMap floodMap, Canvas wrapper, boolean digitize) {
+        this(null, floodMap, wrapper, digitize);
+    }
+
+
+    public MapToolbar(
+        MapOutputTab   mapTab,
+        FloodMap       floodMap,
+        Canvas         wrapper,
+        boolean        digitize)
+    {
+        super(mapTab);
+
+        setWidth100();
+        setHeight(30);
+        setMembersMargin(10);
+        setPadding(5);
+        setBorder("1px solid black");
+
+        this.floodMap = floodMap;
+
+        Canvas spacer = new Canvas();
+        spacer.setWidth("*");
+
+        zoomToMaxButton = createMaxExtentControl();
+        zoomBoxButton   = createZoomBoxControl();
+        zoomOutButton   = createZoomOutControl();
+        panButton       = createPanControl();
+        drawControl     = createDrawControl();
+        selectButton    = createSelectFeatureControl();
+        measureControl  = createMeasureControl();
+        position        = createMousePosition(wrapper);
+        removeButton    = createRemoveFeatureControl();
+        elevationButton = createElevationControl();
+
+        if (mapTab != null) {
+            datacageButton = createDatacageControl();
+            addMember(datacageButton);
+        }
+
+        addMember(zoomToMaxButton);
+        addMember(zoomBoxButton);
+        addMember(zoomOutButton);
+        addMember(panButton);
+
+        if (digitize) {
+            addMember(drawControl);
+            addMember(selectButton);
+            addMember(removeButton);
+            addMember(elevationButton);
+        }
+
+        addMember(measureControl);
+        addMember(spacer);
+        addMember(position);
+    }
+
+
+    protected Map getMap() {
+        return floodMap.getMap();
+    }
+
+
+    protected void activatePan(boolean activate) {
+        if (activate) {
+            panButton.select();
+            pan.activate();
+        }
+        else {
+            panButton.deselect();
+            pan.deactivate();
+        }
+    }
+
+
+    protected void activateZoomBox(boolean activate) {
+        if (activate) {
+            zoomBoxButton.select();
+            zoomBox.activate();
+        }
+        else {
+            zoomBoxButton.deselect();
+            zoomBox.deactivate();
+        }
+    }
+
+
+    protected void activateDrawFeature(boolean activate) {
+        drawControl.activate(activate);
+    }
+
+
+    protected void activateSelectFeature(boolean activate) {
+        if (activate) {
+            selectButton.select();
+            selectFeature.activate();
+        }
+        else {
+            selectButton.deselect();
+            selectFeature.deactivate();
+        }
+    }
+
+
+    protected void activateMeasureControl(boolean activate) {
+        measureControl.activate(activate);
+    }
+
+
+    protected ImgButton createButton(String img, ClickHandler handler) {
+        ImgButton btn = new ImgButton();
+
+        String baseUrl = GWT.getHostPageBaseURL();
+        btn.setSrc(baseUrl + img);
+        btn.setWidth(20);
+        btn.setHeight(20);
+        btn.setShowDown(false);
+        btn.setShowRollOver(false);
+        btn.setShowDisabled(false);
+        btn.setShowDisabledIcon(true);
+        btn.setShowDownIcon(false);
+        btn.setShowFocusedIcon(false);
+
+        if (handler != null) {
+            btn.addClickHandler(handler);
+        }
+
+        return btn;
+    }
+
+
+    protected ImgButton createToggleButton(
+        String img,
+        final EnableDisableCmd cmd
+    ) {
+        final ImgButton btn = new ImgButton();
+
+        String baseUrl = GWT.getHostPageBaseURL();
+        btn.setSrc(baseUrl + img);
+        btn.setActionType(SelectionType.CHECKBOX);
+        btn.setSize(20);
+        btn.setShowRollOver(false);
+        btn.setSelected(false);
+        btn.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent e) {
+                if (btn.isSelected()) {
+                    cmd.enable();
+                }
+                else {
+                    cmd.disable();
+                }
+            }
+        });
+
+        return btn;
+    }
+
+
+    protected ImgButton createMaxExtentControl() {
+        ImgButton zoomToMax = createButton(MSG.zoom_all(), new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                floodMap.getMap().zoomToMaxExtent();
+            }
+        });
+
+        zoomToMax.setTooltip(MSG.zoomMaxExtent());
+
+        return zoomToMax;
+    }
+
+
+    protected ImgButton createZoomBoxControl() {
+        zoomBox = new ZoomBox();
+
+        EnableDisableCmd cmd = new EnableDisableCmd() {
+            public void enable() {
+                activateZoomBox(true);
+                activatePan(false);
+                activateDrawFeature(false);
+                activateSelectFeature(false);
+                activateMeasureControl(false);
+            }
+
+            public void disable() {
+                activateZoomBox(false);
+            }
+        };
+
+        ImgButton button = createToggleButton(MSG.zoom_in(), cmd);
+        button.setTooltip(MSG.zoomIn());
+
+        Map map = getMap();
+        map.addControl(zoomBox);
+
+        return button;
+    }
+
+
+    protected ImgButton createZoomOutControl() {
+        ImgButton zoomOut = createButton(MSG.zoom_out(), new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                Map map   = floodMap.getMap();
+                int level = map.getZoom();
+
+                if (level > 1) {
+                    map.zoomTo(level-1);
+                }
+            }
+        });
+
+        zoomOut.setTooltip(MSG.zoomOut());
+
+        return zoomOut;
+    }
+
+
+    protected ImgButton createPanControl() {
+        pan = new DragPan();
+        getMap().addControl(pan);
+
+        EnableDisableCmd cmd = new EnableDisableCmd() {
+            public void enable() {
+                activateZoomBox(false);
+                activatePan(true);
+                activateDrawFeature(false);
+                activateSelectFeature(false);
+                activateMeasureControl(false);
+            }
+
+            public void disable() {
+                activatePan(false);
+            }
+        };
+
+        final ImgButton button = createToggleButton(MSG.pan(), cmd);
+        button.setTooltip(MSG.moveMap());
+
+        return button;
+    }
+
+
+    protected DrawControl createDrawControl() {
+        EnableDisableCmd cmd = new EnableDisableCmd() {
+            public void enable() {
+                activateZoomBox(false);
+                activatePan(false);
+                activateDrawFeature(true);
+                activateSelectFeature(false);
+                activateMeasureControl(false);
+            }
+
+            public void disable() {
+                activateDrawFeature(false);
+            }
+        };
+        return new DrawControl(getMap(), floodMap.getBarrierLayer(), cmd);
+    }
+
+
+    protected ImgButton createSelectFeatureControl() {
+        SelectFeatureOptions opts = new SelectFeatureOptions();
+        opts.setBox(true);
+
+        // VectorFeatures selected by the SelectFeature control are manually
+        // marked with the string "mark.delete". The control to remove selected
+        // features makes use of this string to determine if the feature should
+        // be deleted (is marked) or not. Actually, we would like to use the
+        // OpenLayers native mechanism to select features, but for some reason
+        // this doesn't work here. After a feature has been selected, the layer
+        // still has no selected features.
+        opts.onSelect(new SelectFeature.SelectFeatureListener() {
+            public void onFeatureSelected(VectorFeature feature) {
+                floodMap.selectFeature(feature);
+            }
+        });
+
+        opts.onUnSelect(new SelectFeature.UnselectFeatureListener() {
+            public void onFeatureUnselected(VectorFeature feature) {
+                floodMap.disableFeature(feature);
+            }
+        });
+
+        selectFeature = new SelectFeature(floodMap.getBarrierLayer(), opts);
+        getMap().addControl(selectFeature);
+
+        EnableDisableCmd cmd = new EnableDisableCmd() {
+            public void enable() {
+                activateDrawFeature(false);
+                activatePan(false);
+                activateZoomBox(false);
+                activateSelectFeature(true);
+                activateMeasureControl(false);
+            }
+
+            public void disable() {
+                activateSelectFeature(false);
+                floodMap.disableFeatures();
+            }
+        };
+
+        ImgButton button = createToggleButton(MSG.selectFeature(), cmd);
+        button.setTooltip(MSG.selectObject());
+
+        return button;
+    }
+
+
+    protected ImgButton createRemoveFeatureControl() {
+        ImgButton remove = createButton(MSG.removeFeature(),new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                Vector          barriers = floodMap.getBarrierLayer();
+                VectorFeature[] features = barriers.getFeatures();
+
+                if (features == null || features.length == 0) {
+                    return;
+                }
+
+                for (int i = features.length-1; i >= 0; i--) {
+                    VectorFeature feature = features[i];
+
+                    Attributes attr = feature.getAttributes();
+                    int del = attr.getAttributeAsInt(FloodMap.MARK_SELECTED);
+
+                    if (del == 1) {
+                        barriers.removeFeature(feature);
+                        feature.destroy();
+                    }
+                }
+            }
+        });
+
+        remove.setTooltip(MSG.removeObject());
+
+        return remove;
+    }
+
+
+    protected ImgButton createElevationControl() {
+        ImgButton btn = createButton(MSG.adjustElevation(), new ClickHandler() {
+            public void onClick(ClickEvent evt) {
+                Vector          barriers = floodMap.getBarrierLayer();
+                VectorFeature[] features = barriers.getFeatures();
+
+                VectorFeature feature = null;
+
+                if (features == null || features.length == 0) {
+                    SC.warn(MSG.error_no_feature_selected());
+                    return;
+                }
+
+                boolean multipleFeatures = false;
+
+                for (VectorFeature f: features) {
+                    Attributes attr = f.getAttributes();
+                    if (attr.getAttributeAsInt(FloodMap.MARK_SELECTED) == 1) {
+                        if (feature == null) {
+                            feature = f;
+                        }
+                        else {
+                            multipleFeatures = true;
+                        }
+                    }
+                }
+
+                if (feature == null) {
+                    SC.warn(MSG.error_no_feature_selected());
+                    return;
+                }
+
+                new ElevationWindow(floodMap, feature).show();
+
+                if (multipleFeatures) {
+                    SC.warn(MSG.warning_use_first_feature());
+                }
+            }
+        });
+
+        btn.setTooltip(MSG.adjustElevationTooltip());
+
+        return btn;
+    }
+
+
+    protected Canvas createMousePosition(Canvas mapWrapper) {
+        return new MapPositionPanel(floodMap.getMapWidget(), mapWrapper);
+    }
+
+
+    protected MeasureControl createMeasureControl() {
+        EnableDisableCmd cmd = new EnableDisableCmd() {
+            public void enable() {
+                activateDrawFeature(false);
+                activatePan(false);
+                activateZoomBox(false);
+                activateSelectFeature(false);
+            }
+
+            public void disable() {
+                // do nothing
+            }
+        };
+
+        return new MeasureControl(floodMap, cmd);
+    }
+
+
+    protected Button createDatacageControl() {
+        Button btn = new Button(MSG.databasket());
+        btn.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent evt) {
+                openDatacageWindow((MapOutputTab) getOutputTab());
+            }
+        });
+
+        return btn;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/map/MeasureControl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,213 @@
+package de.intevation.flys.client.client.ui.map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.types.SelectionType;
+import com.smartgwt.client.widgets.ImgButton;
+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 org.gwtopenmaps.openlayers.client.control.Measure;
+import org.gwtopenmaps.openlayers.client.event.MeasureEvent;
+import org.gwtopenmaps.openlayers.client.event.MeasureListener;
+import org.gwtopenmaps.openlayers.client.event.MeasurePartialListener;
+import org.gwtopenmaps.openlayers.client.handler.PathHandler;
+import org.gwtopenmaps.openlayers.client.handler.PolygonHandler;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.utils.EnableDisableCmd;
+
+
+public class MeasureControl extends HLayout {
+
+    public static final String NUMBER_FORMAT_PATTERN = "#.##";
+
+    public static final String AREA_UNIT      = "ha";
+    public static final int    AREA_FACTOR_M  = 10000;
+    public static final int    AREA_FACTOR_KM = 100;
+
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected FloodMap floodMap;
+
+    protected Measure measureLine;
+    protected Measure measurePolygon;
+
+    protected ImgButton measureLineButton;
+    protected ImgButton measurePolyButton;
+    protected Label     label;
+
+    protected NumberFormat formatter;
+
+
+    public MeasureControl(FloodMap floodMap, EnableDisableCmd cmd) {
+        this.floodMap = floodMap;
+
+        measureLineButton = createMeasureLineControl(cmd);
+        measurePolyButton = createMeasurePolyControl(cmd);
+
+        formatter = NumberFormat.getFormat(NUMBER_FORMAT_PATTERN);
+
+        label = new Label();
+
+        initLayout();
+    }
+
+
+    protected void initLayout() {
+        setMembersMargin(2);
+        addMember(measureLineButton);
+        addMember(measurePolyButton);
+        addMember(label);
+    }
+
+
+    protected ImgButton createMeasureLineControl(final EnableDisableCmd cmd) {
+        measureLine = new Measure(new PathHandler());
+        measureLine.setPersist(true);
+        measureLine.addMeasureListener(new MeasureListener() {
+            public void onMeasure(MeasureEvent e) {
+                updateMeasure(e.getMeasure(), e.getUnits());
+            }
+        });
+        measureLine.addMeasurePartialListener(new MeasurePartialListener() {
+            public void onMeasurePartial(MeasureEvent e) {
+                updateMeasure(e.getMeasure(), e.getUnits());
+            }
+        });
+
+        floodMap.getMap().addControl(measureLine);
+
+        final ImgButton btn = new ImgButton();
+        String baseUrl = GWT.getHostPageBaseURL();
+        btn.setSrc(baseUrl + MSG.measureLine());
+        btn.setActionType(SelectionType.CHECKBOX);
+        btn.setSize(20);
+        btn.setShowRollOver(false);
+        btn.setSelected(false);
+        btn.setTooltip(MSG.measureDistance());
+        btn.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent e) {
+                if (btn.isSelected()) {
+                    cmd.enable();
+                    activateMeasurePolygon(false);
+                    activateMeasureLine(true);
+                }
+                else {
+                    cmd.disable();
+                    activateMeasureLine(false);
+                }
+            }
+        });
+
+        return btn;
+    }
+
+
+    protected ImgButton createMeasurePolyControl(final EnableDisableCmd cmd) {
+        measurePolygon = new Measure(new PolygonHandler());
+        measurePolygon.setPersist(true);
+        measurePolygon.addMeasureListener(new MeasureListener() {
+            public void onMeasure(MeasureEvent e) {
+                updateMeasureArea(e.getMeasure(), e.getUnits());
+            }
+        });
+        measurePolygon.addMeasurePartialListener(new MeasurePartialListener() {
+            public void onMeasurePartial(MeasureEvent e) {
+                updateMeasureArea(e.getMeasure(), e.getUnits());
+            }
+        });
+
+        floodMap.getMap().addControl(measurePolygon);
+
+        final ImgButton btn = new ImgButton();
+        String baseUrl = GWT.getHostPageBaseURL();
+        btn.setSrc(baseUrl + MSG.measurePolygon());
+        btn.setActionType(SelectionType.CHECKBOX);
+        btn.setSize(20);
+        btn.setShowRollOver(false);
+        btn.setSelected(false);
+        btn.setTooltip(MSG.measureArea());
+        btn.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent e) {
+                if (btn.isSelected()) {
+                    cmd.enable();
+                    activateMeasureLine(false);
+                    activateMeasurePolygon(true);
+                }
+                else {
+                    cmd.disable();
+                    activateMeasurePolygon(false);
+                }
+            }
+        });
+
+        return btn;
+    }
+
+
+    protected void clearMeasure() {
+        label.setContents("");
+    }
+
+
+    protected void updateMeasure(float value, String unit) {
+        label.setContents(formatter.format(value) + " " + unit);
+    }
+
+
+    protected void updateMeasureArea(float value, String unit) {
+        float  ha      = value;
+        String ha_unit = unit;
+
+        if (unit.equals("m")) {
+            ha      = (float) value / AREA_FACTOR_M;
+            ha_unit = AREA_UNIT;
+        }
+        else if (unit.equals("km")) {
+            ha      = (float) value * AREA_FACTOR_KM;
+            ha_unit = AREA_UNIT;
+        }
+
+        label.setContents(formatter.format(ha) + " " + ha_unit);
+    }
+
+
+    public void activate(boolean activate) {
+        if (!activate) {
+            clearMeasure();
+            activateMeasureLine(activate);
+            activateMeasurePolygon(activate);
+        }
+    }
+
+
+    protected void activateMeasureLine(boolean activate) {
+        if (activate) {
+            clearMeasure();
+            measureLineButton.select();
+            measureLine.activate();
+        }
+        else {
+            measureLineButton.deselect();
+            measureLine.deactivate();
+        }
+    }
+
+
+    protected void activateMeasurePolygon(boolean activate) {
+        if (activate) {
+            clearMeasure();
+            measurePolyButton.select();
+            measurePolygon.activate();
+        }
+        else {
+            measurePolyButton.deselect();
+            measurePolygon.deactivate();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/range/DistanceInfoDataSource.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,53 @@
+package de.intevation.flys.client.client.ui.range;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.data.DataSource;
+import com.smartgwt.client.data.DataSourceField;
+import com.smartgwt.client.types.DSDataFormat;
+import com.smartgwt.client.types.FieldType;
+
+
+public class DistanceInfoDataSource extends DataSource {
+
+    public static final String XPATH_DISTANCE_DEFAULT = "/distances/distance";
+
+
+    public DistanceInfoDataSource(String url, String river, String filter) {
+        setDataFormat(DSDataFormat.XML);
+        setRecordXPath(XPATH_DISTANCE_DEFAULT);
+
+        DataSourceField desc = new DataSourceField(
+            "description", FieldType.TEXT, "description");
+
+        DataSourceField from = new DataSourceField(
+            "from", FieldType.TEXT, "from");
+
+        DataSourceField to = new DataSourceField(
+            "to", FieldType.TEXT, "to");
+
+        DataSourceField side = new DataSourceField(
+            "riverside", FieldType.TEXT, "riverside");
+
+        DataSourceField top = new DataSourceField(
+            "top", FieldType.TEXT, "top");
+
+        DataSourceField bottom = new DataSourceField(
+            "bottom", FieldType.TEXT, "bottom");
+
+        setFields(desc, from, to, side, top, bottom);
+        setDataURL(getServiceURL(url, river, filter));
+    }
+
+
+    protected String getServiceURL(String server, String river, String filter) {
+        String url = GWT.getModuleBaseURL();
+        url += "distanceinfoxml";
+        url += "?server=" + server;
+        url += "&river=" + river;
+        url += "&filter=" + filter;
+
+        return 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/client/ui/range/LocationsTable.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,94 @@
+package de.intevation.flys.client.client.ui.range;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+
+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 com.smartgwt.client.widgets.grid.ListGridRecord;
+
+import com.smartgwt.client.widgets.grid.CellFormatter;
+
+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 LocationsTable extends ListGrid {
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** The interface that provides the image resources. */
+    private FLYSImages IMAGES = GWT.create(FLYSImages.class);
+
+
+    public LocationsTable() {
+        setWidth100();
+        setHeight100();
+        setSelectionType(SelectionStyle.SINGLE);
+        setSelectionType(SelectionStyle.SINGLE);
+        setShowHeaderContextMenu(false);
+        setShowRecordComponents(true);
+        setShowRecordComponentsByCell(true);
+        setEmptyMessage(MSG.empty_filter());
+        setCanReorderFields(false);
+
+        ListGridField addfrom = new ListGridField ("", "");
+        addfrom.setType(ListGridFieldType.ICON);
+        addfrom.setWidth(20);
+        addfrom.setCellIcon(IMAGES.markerGreen().getURL());
+
+        ListGridField addto = new ListGridField("", "");
+        addto.setType(ListGridFieldType.ICON);
+        addto.setWidth(20);
+        addto.setCellIcon(IMAGES.markerRed().getURL());
+
+        ListGridField ldescr = new ListGridField(
+            "description", MSG.description());
+        ldescr.setType(ListGridFieldType.TEXT);
+        ldescr.setWidth("*");
+
+        ListGridField lside = new ListGridField("riverside", MSG.riverside());
+        lside.setType(ListGridFieldType.TEXT);
+        lside.setWidth("12%");
+
+        ListGridField loc = new ListGridField("from", MSG.locations());
+        loc.setType(ListGridFieldType.FLOAT);
+        loc.setCellFormatter(new CellFormatter() {
+            public String format(
+                Object value,
+                ListGridRecord record,
+                int rowNum, int colNum) {
+                    if (value == null) return null;
+                    try {
+                        NumberFormat nf;
+                        double v = Double.parseDouble((String)value);
+                        nf = NumberFormat.getFormat("###0.00##");
+                        return nf.format(v);
+                    }
+                    catch (Exception e) {
+                        return value.toString();
+                    }
+                }
+            }
+        );
+
+        loc.setWidth("12%");
+
+        ListGridField bottom = new ListGridField("bottom", MSG.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth("10%");
+
+        ListGridField top = new ListGridField("top", MSG.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth("10%");
+
+        setFields(addfrom, addto, ldescr, loc, lside, bottom, 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/client/ui/range/RangeTable.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,113 @@
+package de.intevation.flys.client.client.ui.range;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+
+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 com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.CellFormatter;
+
+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 RangeTable extends ListGrid {
+
+    /** 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);
+
+
+    public RangeTable() {
+        setWidth100();
+        setHeight100();
+        setSelectionType(SelectionStyle.SINGLE);
+        setSelectionType(SelectionStyle.SINGLE);
+        setShowHeaderContextMenu(false);
+        setShowRecordComponents(true);
+        setShowRecordComponentsByCell(true);
+        setEmptyMessage(MESSAGES.empty_filter());
+        setCanReorderFields(false);
+
+        ListGridField addDistance = new ListGridField ("", "");
+        addDistance.setType (ListGridFieldType.ICON);
+        addDistance.setWidth (20);
+        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.FLOAT);
+        from.setCellFormatter(new CellFormatter() {
+            public String format(
+                Object value,
+                ListGridRecord record,
+                int rowNum, int colNum) {
+                    if (value == null) return null;
+                    try {
+                        NumberFormat nf;
+                        double v = Double.parseDouble((String)value);
+                        nf = NumberFormat.getFormat("###0.00##");
+                        return nf.format(v);
+                    }
+                    catch (Exception e) {
+                        return value.toString();
+                    }
+                }
+            }
+        );
+
+        from.setWidth("12%");
+
+        ListGridField to = new ListGridField("to", MESSAGES.to());
+        to.setType(ListGridFieldType.FLOAT);
+        to.setCellFormatter(new CellFormatter() {
+            public String format(
+                Object value,
+                ListGridRecord record,
+                int rowNum, int colNum) {
+                    if (value == null) return null;
+                    GWT.log((String)value);
+                    try {
+                        NumberFormat nf;
+                        double v = Double.parseDouble((String)value);
+                        nf = NumberFormat.getFormat("###0.00##");
+                        return nf.format(v);
+                    }
+                    catch (Exception e) {
+                        return value.toString();
+                    }
+                }
+            }
+        );
+
+        to.setWidth("12%");
+
+        ListGridField dside = new ListGridField(
+            "riverside", MESSAGES.riverside());
+        dside.setType(ListGridFieldType.TEXT);
+        dside.setWidth("12%");
+
+        ListGridField bottom = new ListGridField(
+            "bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth("10%");
+
+        ListGridField top = new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth("10%");
+
+        setFields(addDistance, ddescr, from, to, dside, bottom, 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/client/ui/wq/QDTable.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,80 @@
+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);
+
+    protected boolean lockClick;
+
+    public QDTable() {
+        setWidth100();
+        setHeight100();
+        setSelectionType(SelectionStyle.SINGLE);
+        setSelectionType(SelectionStyle.SINGLE);
+        setShowHeaderContextMenu(false);
+        setShowRecordComponents(true);
+        setShowRecordComponentsByCell(true);
+        setEmptyMessage(MESSAGE.empty_table());
+
+        ListGridField addMax = new ListGridField("max", "");
+        addMax.setType(ListGridFieldType.ICON);
+        addMax.setWidth(30);
+        addMax.setCellIcon(IMAGES.markerRed().getURL());
+
+        ListGridField addMin = new ListGridField("min", "");
+        addMin.setType(ListGridFieldType.ICON);
+        addMin.setWidth(30);
+        addMin.setCellIcon(IMAGES.markerGreen().getURL());
+
+        ListGridField name = new ListGridField("name", MESSAGE.discharge());
+        name.setType(ListGridFieldType.TEXT);
+        name.setWidth("*");
+
+        ListGridField type = new ListGridField("type", MESSAGE.type());
+        type.setType(ListGridFieldType.TEXT);
+        type.setWidth("20%");
+
+        ListGridField value = new ListGridField("value", MESSAGE.wq_value());
+        value.setType(ListGridFieldType.FLOAT);
+        value.setWidth("20%");
+
+        setFields(addMax, addMin, name, type, value);
+    }
+
+    public void hideIconFields () {
+        hideField("max");
+        hideField("min");
+        lockClick = true;
+    }
+
+
+    public void showIconFields() {
+        showField("max");
+        showField("min");
+        lockClick = false;
+    }
+
+    public boolean isLocked() {
+        return lockClick;
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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.FLOAT);
+        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/client/utils/EnableDisableCmd.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.client.client.utils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface EnableDisableCmd {
+
+    void enable();
+
+    void disable();
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,34 @@
+package de.intevation.flys.client.server;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(AddArtifactService.class);
+
+
+    public Collection add(
+        Collection collection,
+        Artifact   artifact,
+        String     url,
+        String     locale)
+    throws ServerException
+    {
+        logger.info("AddArtifactServiceImpl.add");
+
+        return CollectionHelper.addArtifact(collection, artifact, url, locale);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,86 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger = Logger.getLogger(AdvanceService.class);
+
+
+    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
+    {
+        logger.info("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) {
+            logger.error(ce, ce);
+        }
+
+        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:51 2012 +0200
@@ -0,0 +1,507 @@
+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 org.apache.log4j.Logger;
+
+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.Recommendation;
+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 {
+
+    private static final Logger logger =
+        Logger.getLogger(ArtifactDescriptionFactory.class);
+
+
+    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";
+
+    public static final String XPATH_RECOMMENDED_ARTIFACTS =
+        "/art:result/art:recommended-artifacts//*[@factory]";
+
+    /**
+     * 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) {
+        logger.debug("ArtifactDescriptionFactory.createArtifactDescription");
+
+        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);
+
+        logger.debug("Current state name: " + state);
+
+        DataList currentData = extractCurrentData(dynamicNode, state);
+        DataList[] old       = extractOldData(staticNode);
+        String[] states      = extractReachableStates(reachable);
+        OutputMode[] outs    = extractOutputModes(outputs);
+        Recommendation[] rec = extractRecommendedArtifacts(doc);
+
+        return new DefaultArtifactDescription(
+            old,
+            currentData,
+            state,
+            states,
+            outs,
+            rec);
+    }
+
+
+    /**
+     * 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) {
+        logger.debug("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) {
+        logger.debug("ArtifactDescriptionFactory.extractDefaultDataItem");
+
+        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) {
+        logger.debug("ArtifactDescriptionFactory.extractCurrentDataItems");
+
+        if (items == null || items.getLength() == 0) {
+            logger.debug("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) {
+        logger.debug("ArtifactDescriptionFactory.extractMinMaxQValues");
+
+        if (item == null) {
+            logger.debug("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) {
+            logger.debug("No min/max Q values found.");
+            return null;
+        }
+
+        return extractMinMaxValues(node);
+    }
+
+
+    protected static double[] extractMinMaxWValues(Node item) {
+        logger.debug("ArtifactDescriptionFactory.extractMinMaxWValues");
+
+        if (item == null) {
+            logger.debug("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) {
+            logger.debug("No min/max W values found.");
+            return null;
+        }
+
+        return extractMinMaxValues(node);
+    }
+
+
+    protected static double[] extractMinMaxValues(Node node) {
+        logger.debug("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) {
+            logger.debug("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) {
+            logger.debug("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) {
+        logger.debug("ArtifactDescriptionFactory.extractOldData()");
+
+        NodeList stateNodes = (NodeList) XMLUtils.xpath(
+            staticNode,
+            XPATH_STATIC_STATE_NODE,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (stateNodes == null || stateNodes.getLength() == 0) {
+            logger.debug("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) {
+            logger.debug("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) {
+        logger.debug("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) {
+        logger.debug("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 {
+                logger.debug("Found an invalid output mode.");
+            }
+        }
+
+        return (OutputMode[]) outs.toArray(new OutputMode[size]);
+    }
+
+
+    protected static Recommendation[] extractRecommendedArtifacts(Document doc){
+        logger.debug("ArtifactDescriptionFactory.extractRecommendedArtifacts.");
+
+        NodeList list = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_RECOMMENDED_ARTIFACTS,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = list != null ? list.getLength() : 0;
+
+        Recommendation[] rec = new Recommendation[num];
+
+        for (int i = 0; i < num; i++) {
+            Element e       = (Element) list.item(i);
+            String  factory = e.getAttribute("factory");
+            String  index   = e.getAttribute("ids");
+
+            if (factory != null && factory.length() > 0) {
+                rec[i] = new Recommendation(factory, index);
+            }
+        }
+
+        return 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/server/ArtifactHelper.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,111 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.CreationFilter;
+
+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.shared.model.Recommendation;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ArtifactHelper {
+
+    private static final Logger logger = Logger.getLogger(ArtifactHelper.class);
+
+
+    /** The error message key that is thrown if an error occured while artifact
+     * creation.*/
+    public static final String ERROR_CREATE_ARTIFACT = "error_create_artifact";
+
+
+    private ArtifactHelper() {
+    }
+
+
+    /**
+     * @param factory ArtifactFactory to use.
+     */
+    public static Artifact createArtifact(
+        String         serverUrl,
+        String         locale,
+        String         factory,
+        Recommendation recommendation)
+    throws ServerException
+    {
+        logger.debug("ArtifactHelper.create");
+
+        String         uuid;
+        String         ids;
+        CreationFilter filter;
+
+        if (recommendation != null) {
+            uuid   = recommendation.getMasterArtifact();
+            ids    = recommendation.getIDs();
+            filter = convertFilter(recommendation.getFilter());
+        }
+        else {
+            uuid   = null;
+            ids    = null;
+            filter = null;
+        }
+
+        Document create = ClientProtocolUtils.newCreateDocument(
+            factory, uuid, ids, filter);
+
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            return (Artifact) client.create(create, new FLYSArtifactCreator());
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        throw new ServerException(ERROR_CREATE_ARTIFACT);
+    }
+
+
+    /**
+     * Create CreationFilter from Recommendation.Filter.
+     */
+    public static CreationFilter convertFilter(Recommendation.Filter filter) {
+
+        if (filter == null) {
+            return null;
+        }
+
+        CreationFilter cf = new CreationFilter();
+
+        Map<String, List<Recommendation.Facet>> outs = filter.getOuts();
+
+        for (Map.Entry<String, List<Recommendation.Facet>> entry:
+            outs.entrySet()) {
+            List<Recommendation.Facet> rfs = entry.getValue();
+            List<CreationFilter.Facet> cfs =
+                new ArrayList<CreationFilter.Facet>(rfs.size());
+            for (Recommendation.Facet rf: rfs) {
+                cfs.add(new CreationFilter.Facet(rf.getName(), rf.getIndex()));
+            }
+            cf.add(entry.getKey(), cfs);
+        }
+
+        return cf;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,50 @@
+package de.intevation.flys.client.server;
+
+import org.apache.log4j.Logger;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.client.services.ArtifactService;
+
+import de.intevation.flys.client.shared.model.Recommendation;
+
+/**
+ * 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
+{
+    private static final Logger logger =
+        Logger.getLogger(ArtifactServiceImpl.class);
+
+
+    /**
+     * Creates new Artifacts based on a given Recommendation and factory.
+     * <b>Note, that all the work is done in ArtifactHelper!</b>
+     *
+     * @param serverUrl The URL of the ArtifactServer.
+     * @param locale The locale used for HTTP request.
+     * @param factory The factory that is used to create the new Artifact.
+     *
+     * @return a new Artifact.
+     */
+    public Artifact create(
+        String         serverUrl,
+        String         locale,
+        String         factory,
+        Recommendation recom
+    )
+    throws ServerException
+    {
+        logger.info("ArtifactServiceImpl.create");
+
+        return ArtifactHelper.createArtifact(serverUrl, locale, factory, recom);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/BaseServlet.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,58 @@
+package de.intevation.flys.client.server;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+
+public class BaseServlet extends HttpServlet {
+
+    private static Logger logger = Logger.getLogger(BaseServlet.class);
+
+
+    public static final String LOG4J_PROPERTIES = "FLYS_CLIENT_LOG4J_PROPERIES";
+
+
+    @Override
+    public void init()
+    throws ServletException
+    {
+        System.out.println("BaseServlet.init");
+
+        initLogging();
+        initConfigParameters();
+    }
+
+
+    protected void initLogging() {
+        String log4jProperties = System.getenv(LOG4J_PROPERTIES);
+
+        if (log4jProperties == null || log4jProperties.length() == 0) {
+            String file = getInitParameter("log4j-properties");
+
+            if (file != null && file.length() > 0) {
+                log4jProperties = getServletContext().getRealPath(file);
+            }
+        }
+
+        if (log4jProperties != null && log4jProperties.length() > 0) {
+            PropertyConfigurator.configure(log4jProperties);
+            logger.info("Log4J logging initialized.");
+        }
+        else {
+            System.out.println("Error while setting up Log4J configuration.");
+        }
+
+        System.out.println("BaseServlet.init finished");
+    }
+
+
+    protected void initConfigParameters() {
+        String url = getInitParameter("server-url");
+        logger.debug("Found server url: " + url);
+
+        getServletContext().setAttribute("server-url", url);
+    }
+}
--- /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:51 2012 +0200
@@ -0,0 +1,80 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(CSVExportServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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) {
+            logger.error(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:51 2012 +0200
@@ -0,0 +1,260 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(ChartInfoServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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();
+        }
+
+        logger.debug("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) {
+        logger.debug("ChartInfoServiceImpl.parseXAxes");
+
+        NodeList axes = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_X_AXES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return parseAxes(axes);
+    }
+
+
+    protected Axis[] parseYAxes(Document doc) {
+        logger.debug("ChartInfoServiceImpl.parseYAxes");
+
+        NodeList axes = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_Y_AXES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return parseAxes(axes);
+    }
+
+
+    protected Axis[] parseAxes(NodeList axes) {
+        logger.debug("ChartInfoServiceImpl.parseAxes");
+
+        int count = axes != null ? axes.getLength() : 0;
+
+        logger.debug("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
+                    logger.debug("The axis is out of valid range: " + pos);
+                    continue;
+                }
+
+                result[pos] = new Axis(pos, from, to, min, max);
+            }
+            catch (NumberFormatException nfe) {
+                nfe.printStackTrace();
+            }
+        }
+
+        logger.debug("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) {
+        logger.debug("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) {
+                logger.warn("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) {
+                logger.warn("Error while parsing matrix values.");
+            }
+        }
+
+        logger.warn("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:51 2012 +0200
@@ -0,0 +1,135 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(ChartOutputServiceImpl.class);
+
+
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
+        logger.info("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");
+
+            prepareHeader(req, resp);
+
+            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) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+    }
+
+
+    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"));
+        attr.put("format", req.getParameter("format"));
+
+        return attr;
+    }
+
+
+    protected void prepareHeader(
+        HttpServletRequest  req,
+        HttpServletResponse resp
+    ) {
+        String export = req.getParameter("export");
+
+        if (export != null && export.equals("true")) {
+            String format = req.getParameter("format");
+
+            if (format == null || format.length() == 0) {
+                format = "png";
+            }
+
+            String fn = "chart_export" + getFileExtension(format);
+
+            resp.setHeader("Content-Disposition", "attachment;filename=" + fn);
+            resp.setHeader("Content-Type", getMimeType(format));
+        }
+    }
+
+
+    protected String getFileExtension(String format) {
+        if (format.equals("png")) {
+            return ".png";
+        }
+        else if (format.equals("pdf")) {
+            return ".pdf";
+        }
+        else if (format.equals("svg")) {
+            return ".svg";
+        }
+
+        return ".png";
+    }
+
+
+    protected String getMimeType(String format) {
+        if (format.equals("png")) {
+            return "image/png";
+        }
+        else if (format.equals("pdf")) {
+            return "application/pdf";
+        }
+        else if (format.equals("svg")) {
+            return "svg+xml";
+        }
+
+        return "image/png";
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,187 @@
+package de.intevation.flys.client.server;
+
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+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 {
+
+    private static final Logger logger =
+        Logger.getLogger(ChartServiceHelper.class);
+
+
+    /** 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) {
+        logger.debug("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);
+        appendFormat(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)
+    {
+        logger.debug("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)
+    {
+        logger.debug("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)
+    {
+        logger.debug("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);
+        }
+    }
+
+
+    /**
+     * This method extracts the format string from request object and appends
+     * those format - if existing - to the attribute document used to adjust
+     * the chart settings.
+     *
+     * @param req The request object that might contain the chart format.
+     * @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 appendFormat(
+        Map<String, String> req,
+        Element             attributes,
+        ElementCreator      ec
+
+    ) {
+        logger.debug("ChartServiceHelper.appendFormat");
+
+        String formatStr = req.get("format");
+        if (formatStr == null || formatStr.length() == 0) {
+            return;
+        }
+
+        Element format = ec.create("format");
+        ec.addAttr(format, "value", formatStr, true);
+
+        attributes.appendChild(format);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,63 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(CollectionAttributeServiceImpl.class);
+
+
+    public static final String ERROR_UPDATING_COLLECTION_ATTRIBUTE =
+        "error_update_collection_attribute";
+
+
+    public Collection update(Collection collection, String url, String locale)
+    throws ServerException
+    {
+        logger.info("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());
+
+            logger.debug("Collection attribute successfully set.");
+
+            return describe(collection.identifier(), url, locale);
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        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:51 2012 +0200
@@ -0,0 +1,731 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifacts.httpclient.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.AttributedTheme;
+import de.intevation.flys.client.shared.model.ChartMode;
+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.ExportMode;
+import de.intevation.flys.client.shared.model.Facet;
+import de.intevation.flys.client.shared.model.MapMode;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.shared.model.ReportMode;
+import de.intevation.flys.client.shared.model.Recommendation;
+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 {
+
+    private static final Logger logger =
+        Logger.getLogger(CollectionHelper.class);
+
+
+    public static final String ERROR_ADD_ARTIFACT = "error_add_artifact";
+
+    public static final String ERROR_REMOVE_ARTIFACT = "error_remove_artifact";
+
+    public static final String XPATH_FACETS = "art:facets/art:facet";
+
+    public static final String XPATH_LOADED_RECOMMENDATIONS =
+        "/art:artifact-collection/art:attribute/art:loaded-recommendations/art:recommendation";
+
+
+    public static Document createAttribute(Collection collection) {
+        logger.debug("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);
+        Element recs = createRecommendationsElements(cr, collection);
+
+        if (outs != null) {
+            attr.appendChild(outs);
+        }
+
+        if (recs != null) {
+            attr.appendChild(recs);
+        }
+
+        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)
+    {
+        logger.debug("CollectionHelper.createOutputElements");
+
+        java.util.Collection<OutputMode> modes = mmodes != null
+            ? mmodes.values()
+            : null;
+
+        if (modes == null || modes.size() == 0) {
+            logger.debug("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)
+    {
+        logger.debug("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) {
+            logger.debug("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("facet");
+
+        if (theme instanceof AttributedTheme) {
+            AttributedTheme at = (AttributedTheme) theme;
+            Set<String>   keys = at.getKeys();
+
+            for (String key: keys) {
+                cr.addAttr(t, key, at.getAttr(key), true);
+            }
+        }
+        else {
+            cr.addAttr(t, "active", Integer.toString(theme.getActive()), 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);
+            cr.addAttr(t, "visible", Integer.toString(theme.getVisible()), true);
+        }
+
+        return t;
+    }
+
+
+    /**
+     * Creates a whole block with art:loaded-recommendations nodes.
+     *
+     * @param cr The ElementCreator used to create new elements.
+     * @param c  The collection.
+     *
+     * @return an element with loaded recommendations.
+     */
+    protected static Element createRecommendationsElements(
+        ElementCreator cr,
+        Collection     c)
+    {
+        logger.debug("CollectionHelper.createRecommendationsElements");
+
+        List<Recommendation> rs = c.getRecommendations();
+
+        if (rs == null || rs.size() == 0) {
+            logger.debug("Collection did not load recommendations: " +
+                c.identifier());
+            return null;
+        }
+
+        Element loaded = cr.create("loaded-recommendations");
+
+        for (Recommendation r: rs) {
+            Element recommendation = createRecommendationElement(cr, c, r);
+
+            if (recommendation != null) {
+                loaded.appendChild(recommendation);
+            }
+        }
+
+        return loaded;
+    }
+
+
+    /**
+     * Create a node art:recommended.
+     *
+     * @param cr The ElementCreator used to create new elements.
+     * @param c  The collection.
+     * @param r  The Recommendation.
+     *
+     * @return an element that represents an output mode with its themes.
+     */
+    protected static Element createRecommendationElement(
+        ElementCreator cr,
+        Collection     c,
+        Recommendation r)
+    {
+        logger.debug("CollectionHelper.createRecommendationElement");
+
+        Element recommendation = cr.create("recommendation");
+        cr.addAttr(recommendation, "factory", r.getFactory(), true);
+        cr.addAttr(recommendation, "ids", r.getIDs(), true);
+
+        return recommendation;
+    }
+
+
+    /**
+     * Take 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.
+     */
+    public static Collection parseCollection(Document description) {
+        logger.debug("AddArtifactServiceImpl.parseCollection");
+
+        if (description == null) {
+            logger.warn("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);
+
+        String name = XMLUtils.xpathString(
+            description,
+            "art:artifact-collection/@art:name",
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (uuid.length() == 0) {
+            logger.warn("Found an invalid (zero length uuid) Collection!");
+            return null;
+        }
+
+        if (ttlStr.length() == 0) {
+            logger.warn("Found an invalid Collectioni (zero length ttl)!");
+            return null;
+        }
+
+
+        long ttl = -1;
+        try {
+            ttl = Long.valueOf(ttlStr);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        Map<String, ThemeList> themeList = parseThemeLists(description);
+        List<Recommendation> recommended = parseRecommendations(description);
+
+        name = (name != null && name.length() > 0) ? name : uuid;
+
+        Collection c = !themeList.isEmpty()
+            ? new DefaultCollection(uuid, ttl, name, recommended, themeList)
+            : new DefaultCollection(uuid, ttl, name, recommended);
+
+        NodeList items = (NodeList) XMLUtils.xpath(
+            description,
+            "art:artifact-collection/art:artifacts/art:artifact",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (items == null || items.getLength() == 0) {
+            logger.debug("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),
+                i == 0);
+
+            if (item != null) {
+                c.addItem(item);
+            }
+        }
+
+        logger.debug(
+            "Found " + c.getItemLength() + " collection items " +
+            "for the Collection '" + c.identifier() + "'.");
+
+        return c;
+    }
+
+
+    protected static Map<String, ThemeList> parseThemeLists(Document desc) {
+        logger.debug("DescribeCollectionServiceImpl.parseThemeLists");
+
+        NodeList lists = (NodeList) XMLUtils.xpath(
+            desc,
+            "/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 static ThemeList parseThemeList(Element node) {
+        logger.debug("DescribeCollectionServiceImpl.parseThemeList");
+
+        NodeList themes = node.getElementsByTagNameNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "facet");
+
+        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 static Theme parseTheme(Element ele) {
+        logger.debug("DescribeCollectionServiceImpl.parseTheme");
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        NamedNodeMap attrMap = ele.getAttributes();
+        int          attrNum = attrMap != null ? attrMap.getLength() : 0;
+
+        AttributedTheme t = new AttributedTheme();
+
+        for (int i = 0; i < attrNum; i++) {
+            Node attr = attrMap.item(i);
+
+            String prefix = attr.getPrefix();
+            String name   = attr.getNodeName().replace(prefix + ":", "");
+            String value  = attr.getNodeValue();
+
+            t.addAttr(name, value);
+        }
+
+        return t;
+    }
+
+
+    /**
+     * 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 static CollectionItem parseCollectionItem(
+        Element node,
+        boolean outs
+    ) {
+        logger.debug("AddArtifactServiceImpl.parseCollectionItem");
+
+        if (node == null) {
+            logger.debug("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) {
+            logger.warn("Found an invalid CollectionItem!");
+            return null;
+        }
+
+        List<OutputMode> modes = new ArrayList<OutputMode>();
+
+        if (outs) {
+            NodeList outputmodes = node.getElementsByTagNameNS(
+                uri, "outputmodes");
+
+            if (outputmodes.getLength() < 1) {
+                return null;
+            }
+
+            Element om = (Element)outputmodes.item(0);
+
+            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 static List<OutputMode> parseOutputModes(Element node) {
+        logger.debug("AddArtifactServiceImpl.parseOutputModes");
+
+        if (node == null) {
+            logger.debug("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) {
+            logger.debug("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");
+            String type = tmp.getAttributeNS(uri, "type");
+
+            if (name.length() == 0) {
+                logger.warn("Found an invalid output mode.");
+                continue;
+            }
+
+            OutputMode outmode = null;
+            List<Facet> fs     = extractFacets(tmp);
+
+            if (type.equals("export")) {
+                outmode = new ExportMode(name, desc, mime, fs);
+            }
+            else if (type.equals("report")) {
+                outmode = new ReportMode(name, desc, mime, fs);
+            }
+            else if (type.equals("chart")){
+                outmode = new ChartMode(name, desc, mime, fs);
+            }
+            else if (type.equals("map")){
+                outmode = new MapMode(name, desc, mime, fs);
+            }
+            else {
+                logger.warn("Broken Output mode without type found.");
+                continue;
+            }
+
+            modes.add(outmode);
+        }
+
+        return modes;
+    }
+
+
+    protected static List<Facet> extractFacets(Element outmode) {
+        logger.debug("DescribeCollectionServiceImpl - 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;
+    }
+
+
+    public static List<Recommendation> parseRecommendations(Document doc) {
+        logger.debug("DescribeCollectionServiceImpl.parseRecommendations");
+
+        NodeList list = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_LOADED_RECOMMENDATIONS,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = list != null ? list.getLength() : 0;
+
+        List<Recommendation> recs = new ArrayList<Recommendation>(num);
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        for (int i = 0; i < num; i++) {
+            Element rec = (Element) list.item(i);
+
+            String factory = rec.getAttributeNS(uri, "factory");
+            String dbids   = rec.getAttributeNS(uri, "ids");
+
+            if (factory != null && factory.length() > 0) {
+                recs.add(new Recommendation(factory, dbids));
+            }
+        }
+
+        return recs;
+    }
+
+
+    /**
+     * Add an artifact to a collection.
+     * @param collection Collection to add artifact to.
+     * @param artifact   Artifact to add to collection
+     */
+    public static Collection addArtifact(
+        Collection collection,
+        Artifact   artifact,
+        String     url,
+        String     locale)
+    throws ServerException
+    {
+        logger.debug("Collection.addArtifact");
+
+        if (collection == null) {
+            logger.warn("The given Collection is null!");
+            return null;
+        }
+
+        Document add = ClientProtocolUtils.newAddArtifactDocument(
+            artifact.getUuid(), null);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            logger.debug("Do HTTP request now.");
+
+            Document response = (Document) client.doCollectionAction(
+                add, collection.identifier(), new DocumentResponseHandler());
+
+            logger.debug(
+                "Finished HTTP request successfully. Parse Collection now.");
+
+            Collection c = CollectionHelper.parseCollection(response);
+
+            if (c == null) {
+                throw new ServerException(ERROR_ADD_ARTIFACT);
+            }
+
+            return c;
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+
+        throw new ServerException(ERROR_ADD_ARTIFACT);
+    }
+
+
+    /**
+     * Remove an artifact from a collection.
+     * @param collection Collection to remove artifact to.
+     * @param artifact   Artifact to add to collection
+     */
+    public static Collection removeArtifact(
+        Collection collection,
+        String     artifactId,
+        String     url,
+        String     locale)
+    throws ServerException
+    {
+        logger.debug("Collection.removeArtifact");
+
+        if (collection == null) {
+            logger.warn("The given Collection is null!");
+            return null;
+        }
+
+        Document remove = ClientProtocolUtils.newRemoveArtifactDocument(
+            artifactId);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            logger.debug("Do HTTP request now.");
+
+            Document response = (Document) client.doCollectionAction(
+                remove, collection.identifier(), new DocumentResponseHandler());
+
+            logger.debug(
+                "Finished HTTP request successfully. Parse Collection now.");
+            logger.debug(XMLUtils.toString(response));
+
+            Collection c = CollectionHelper.parseCollection(response);
+
+            if (c == null) {
+                throw new ServerException(ERROR_REMOVE_ARTIFACT);
+            }
+
+            return c;
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        throw new ServerException(ERROR_REMOVE_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/CollectionItemAttributeServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,232 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.apache.log4j.Logger;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+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.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.CollectionItemAttributeService;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionItemAttribute;
+import de.intevation.flys.client.shared.model.Style;
+import de.intevation.flys.client.shared.model.StyleSetting;
+
+
+/**
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class CollectionItemAttributeServiceImpl
+extends      RemoteServiceServlet
+implements   CollectionItemAttributeService
+{
+    private static final Logger logger =
+        Logger.getLogger(CollectionItemAttributeServiceImpl.class);
+
+
+    public static final String XPATH_RESULT = "/art:result/text()";
+
+    public static final String OPERATION_FAILURE = "FAILED";
+
+    public static final String ERROR_NO_STYLES_FOUND =
+        "error_no_theme_styles_found";
+
+
+    public CollectionItemAttribute getCollectionItemAttribute(
+        Collection collection,
+        String artifact,
+        String url,
+        String locale)
+    throws ServerException
+    {
+        logger.info(
+            "CollectionItemAttributeServiceImpl.getCollectionItemAttribute");
+
+        Document requestDoc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+                requestDoc,
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = ec.create("action");
+
+        Element type = ec.create("type");
+        ec.addAttr(type, "name", "getitemattribute", false);
+
+        Element art = ec.create("artifact");
+        ec.addAttr(art, "uuid", artifact, false);
+
+        type.appendChild(art);
+        action.appendChild(type);
+        requestDoc.appendChild (action);
+
+        try {
+            HttpClient client = new HttpClientImpl(url, locale);
+            Document res = (Document) client.doCollectionAction(
+                requestDoc,
+                collection.identifier(),
+                new DocumentResponseHandler());
+            return readXML (res, artifact);
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        throw new ServerException(ERROR_NO_STYLES_FOUND);
+    }
+
+
+    public void setCollectionItemAttribute(
+        Collection collection,
+        String artifact,
+        String url,
+        String locale,
+        CollectionItemAttribute attributes)
+    throws ServerException
+    {
+        logger.info(
+            "CollectionItemAttributeServiceImpl.setCollectionItemAttribute");
+
+        Document doc = writeXML(attributes, artifact);
+
+        try {
+            HttpClient client = new HttpClientImpl(url, locale);
+            Document res = (Document) client.doCollectionAction(
+                doc,
+                collection.identifier(),
+                new DocumentResponseHandler());
+
+            return;
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+            throw new ServerException(ce.getLocalizedMessage());
+        }
+    }
+
+    protected CollectionItemAttribute readXML(Document doc, String artifact)
+    throws    ServerException
+    {
+        CollectionItemAttribute cia = new CollectionItemAttribute();
+        cia.setArtifact(artifact);
+
+        Element root = doc.getDocumentElement();
+        NodeList themes = root.getElementsByTagName("art:themes");
+
+        if (themes == null || themes.getLength() == 0) {
+            throw new ServerException(ERROR_NO_STYLES_FOUND);
+        }
+
+        Element e = (Element) themes.item(0);
+        NodeList items = e.getElementsByTagName("theme");
+
+        for (int i = 0; i < items.getLength(); i++) {
+            Style s = getStyle ((Element) items.item(i));
+            if(s == null) {
+                throw new ServerException(ERROR_NO_STYLES_FOUND);
+            }
+            else {
+                cia.appendStyle(s);
+            }
+        }
+
+        return cia;
+    }
+
+
+    protected Document writeXML (
+        CollectionItemAttribute attributes,
+        String artifact)
+    {
+        Document styles = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+                styles,
+                ArtifactNamespaceContext.NAMESPACE_URI,
+                ArtifactNamespaceContext.NAMESPACE_PREFIX);
+        Element action = ec.create("action");
+        Element type = ec.create("type");
+        type.setAttribute("name", "setitemattribute");
+        Element art = ec.create("artifact");
+        art.setAttribute("uuid", artifact);
+        Element attr = ec.create("attribute");
+        Element themes = ec.create("themes");
+        action.appendChild(type);
+        type.appendChild(art);
+        art.appendChild(attr);
+        attr.appendChild(themes);
+
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+                styles,
+                "",
+                "");
+
+        for (int i = 0; i < attributes.getNumStyles(); i++) {
+            Style s = attributes.getStyle(i);
+            Element theme = creator.create("theme");
+            theme.setAttribute("name", s.getName());
+            theme.setAttribute("facet", s.getFacet());
+            theme.setAttribute("index", String.valueOf(s.getIndex()));
+            for (int j = 0; j < s.getNumSettings(); j++) {
+                StyleSetting set = s.getSetting(j);
+                Element field = creator.create("field");
+                field.setAttribute("name", set.getName());
+                field.setAttribute("display", set.getDisplayName());
+                field.setAttribute("default", set.getDefaultValue());
+                field.setAttribute("hints", set.getHints());
+                field.setAttribute("type", set.getType());
+                theme.appendChild(field);
+            }
+            themes.appendChild(theme);
+        }
+        styles.appendChild(action);
+        return styles;
+    }
+
+
+    protected Style getStyle (Element element) {
+
+        if (!element.getTagName().equals("theme")) {
+            return null;
+        }
+
+        NodeList list = element.getElementsByTagName("field");
+        Style style = new Style();
+
+        style.setName (element.getAttribute("name"));
+        style.setFacet (element.getAttribute("facet"));
+        try {
+            int ndx = Integer.parseInt(element.getAttribute("index"));
+            style.setIndex (ndx);
+        }
+        catch(NumberFormatException nfe) {
+            return null;
+        }
+        for(int i = 0; i < list.getLength(); i++) {
+            Element e = (Element) list.item(i);
+            StyleSetting set = new StyleSetting (
+                e.getAttribute("name"),
+                e.getAttribute("default"),
+                e.getAttribute("display"),
+                e.getAttribute("hints"),
+                e.getAttribute("type"));
+            style.appendStyleSetting(set);
+        }
+        return style;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,84 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(CreateCollectionServiceImpl.class);
+
+
+    /** 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
+    {
+        logger.info("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), uuid);
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        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:51 2012 +0200
@@ -0,0 +1,38 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(DeleteCollectionServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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/DescribeArtifactServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,73 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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.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.DescribeArtifactService;
+
+
+/**
+ * This interface provides artifact specific operation DESCRIBE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DescribeArtifactServiceImpl
+extends      RemoteServiceServlet
+implements   DescribeArtifactService
+{
+    private static final Logger logger =
+        Logger.getLogger(DescribeArtifactServiceImpl.class);
+
+
+    public static final String ERROR_DESCRIBE_ARTIFACT =
+        "error_describe_artifact";
+
+
+    public Artifact describe(String url, String locale, Artifact artifact)
+    throws ServerException
+    {
+        logger.info("DescribeArtifactServiceImpl.describe");
+
+        Document describe = ClientProtocolUtils.newDescribeDocument(
+            artifact.getUuid(),
+            artifact.getHash(),
+            true);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            logger.debug("Start Http request now.");
+
+            Document description = (Document) client.describe(
+                new de.intevation.artifacts.httpclient.objects.Artifact(
+                    artifact.getUuid(),
+                    artifact.getHash()),
+                describe,
+                new DocumentResponseHandler());
+
+            if (description != null) {
+                logger.debug("Finished Http request sucessfully!");
+
+                return (Artifact) new FLYSArtifactCreator().create(description);
+            }
+        }
+        catch (ConnectionException ce) {
+            ce.printStackTrace();
+        }
+
+        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/DescribeCollectionServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,74 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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.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.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
+{
+    private static final Logger logger =
+        Logger.getLogger(DescribeCollectionServiceImpl.class);
+
+
+    /** 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 Collection describe(String uuid, String serverUrl, String locale)
+    throws ServerException
+    {
+        logger.info("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 = CollectionHelper.parseCollection(response);
+
+            if (c == null) {
+                throw new ServerException(ERROR_DESCRIBE_COLLECTION);
+            }
+
+            logger.debug("Collection successfully parsed.");
+
+            return c;
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        throw new ServerException(ERROR_DESCRIBE_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/DistanceInfoServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,160 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(DistanceInfoServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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);
+
+            logger.debug("Extract distance info objects now.");
+            DistanceInfoObject[] objects = extractDistanceInfoObjects(result);
+
+            if (objects != null && objects.length > 0) {
+                return objects;
+            }
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        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) {
+            logger.warn("No distance info found.");
+            throw new ServerException(ERROR_NO_DISTANCEINFO_FOUND);
+        }
+
+        int num = list.getLength();
+        logger.debug("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);
+            }
+        }
+
+        logger.debug("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) {
+                logger.warn(nfe.getLocalizedMessage());
+            }
+        }
+
+        logger.warn("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/DistanceInfoXML.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,89 @@
+package de.intevation.flys.client.server;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.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.StreamResponseHandler;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceInfoXML
+extends      HttpServlet
+{
+    private static final Logger logger = Logger.getLogger(DistanceInfoXML.class);
+
+
+    public static final String ERROR_NO_DISTANCEINFO_FOUND =
+        "error_no_distanceinfo_found";
+
+
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
+        logger.info("DistanceInfoXML.doGet");
+
+        String river  = req.getParameter("river");
+        String url    = req.getParameter("server");
+        String filter = req.getParameter("filter");
+
+        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);
+
+        if (filter != null && filter.length() > 0) {
+            Element typeEl = ec.create("filter");
+            typeEl.setTextContent(filter);
+
+            riverEl.appendChild(typeEl);
+        }
+
+        HttpClient client = new HttpClientImpl(url);
+
+        try {
+            InputStream in = (InputStream) client.callService(
+                url, "distanceinfo", doc, new StreamResponseHandler());
+
+            OutputStream out = resp.getOutputStream();
+
+            byte[] b = new byte[4096];
+            int i;
+            while ((i = in.read(b)) >= 0) {
+                out.write(b, 0, i);
+            }
+
+            out.flush();
+            out.close();
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, 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-client/src/main/java/de/intevation/flys/client/server/DoCollectionAction.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,62 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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 {
+
+    private static final Logger logger =
+        Logger.getLogger(DoCollectionAction.class);
+
+
+    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
+    {
+        logger.info("DoCollectionAction.doAction");
+
+        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)) {
+                logger.error("Operation failed.");
+                throw new ServerException(FAILURE_EXCEPTION);
+            }
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+            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:51 2012 +0200
@@ -0,0 +1,64 @@
+package de.intevation.flys.client.server;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(ExportServiceImpl.class);
+
+
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
+        logger.info("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);
+
+            logger.debug("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) {
+            logger.error(ioe, 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-client/src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,181 @@
+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.NodeList;
+
+import org.apache.log4j.Logger;
+
+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.CalculationMessage;
+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 {
+
+    private static final Logger logger =
+        Logger.getLogger(FLYSArtifactCreator.class);
+
+
+    /** 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";
+
+    /** The XPath to the value that determines if the artifact is processing in
+     * background.*/
+    public static final String XPATH_BACKGROUND_VALUE =
+        "/art:result/art:background-processing/@art:value";
+
+    /** The XPath that points to the (if existing) background messages.*/
+    public static final String XPATH_BACKGROUND =
+        "/art:result/art:background-processing";
+
+
+    /**
+     * 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) {
+        logger.debug("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);
+
+        String backgroundStr = XMLUtils.xpathString(
+            doc, XPATH_BACKGROUND_VALUE, ArtifactNamespaceContext.INSTANCE);
+
+        boolean background = false;
+        if (backgroundStr != null && backgroundStr.length() > 0) {
+            background = Boolean.valueOf(backgroundStr);
+        }
+
+        List<CalculationMessage> msg = parseBackgroundMessages(doc);
+
+        logger.debug("NEW Artifact UUID: " + uuid);
+        logger.debug("NEW Artifact HASH: " + hash);
+        logger.debug("NEW Artifact NAME: " + name);
+        logger.debug("NEW Artifact IN BACKGROUND: " + background);
+
+        if (name == null) {
+            return new DefaultArtifact(uuid, hash, background, msg);
+        }
+
+        name = name.trim();
+
+        if (name.length() > 0 && name.equals("winfo")) {
+            logger.debug("+++++ NEW WINFO ARTIFACT.");
+            return new WINFOArtifact(uuid, hash, background, msg);
+        }
+
+        return new DefaultArtifact(uuid, hash, background, msg);
+    }
+
+
+    public static List<CalculationMessage> parseBackgroundMessages(Document d) {
+        NodeList list = (NodeList) XMLUtils.xpath(
+            d, XPATH_BACKGROUND, XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int len = list != null ? list.getLength() : 0;
+
+        logger.debug("Found " + len + " background messages.");
+
+        List<CalculationMessage> res = new ArrayList<CalculationMessage>(len);
+
+        for (int i = 0; i < len; i++) {
+            CalculationMessage msg = parseBackgroundMessage(
+                (Element) list.item(i));
+
+            if (msg != null) {
+                res.add(msg);
+            }
+        }
+
+        return res;
+    }
+
+
+    public static CalculationMessage parseBackgroundMessage(Element e) {
+        String steps       = e.getAttribute("art:steps");
+        String currentStep = e.getAttribute("art:currentStep");
+        String message     = e.getTextContent();
+
+        int lenCurStep = currentStep != null ? currentStep.length() : 0;
+        int lenSteps   = steps       != null ? steps.length()       : 0;
+        int lenMessage = message     != null ? message.length()     : 0;
+
+        if (lenSteps > 0 && lenMessage > 0 && lenCurStep > 0) {
+            try {
+                return new CalculationMessage(
+                    Integer.parseInt(steps),
+                    Integer.parseInt(currentStep),
+                    message);
+
+            }
+            catch (NumberFormatException nfe) {
+                nfe.printStackTrace();
+            }
+        }
+
+        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/FeedServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,138 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.client.services.FeedService;
+
+/**
+ * This interface provides a method that bundles the artifact specific
+ * operation FEED.
+ */
+public class FeedServiceImpl
+extends      RemoteServiceServlet
+implements   FeedService
+{
+    private static final Logger logger = Logger.getLogger(FeedServiceImpl.class);
+
+
+    /** 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 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.
+     */
+    public Artifact feed(
+        String   url,
+        String   locale,
+        Artifact artifact,
+        Data[]   data)
+    throws    ServerException
+    {
+        logger.info("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) {
+                logger.warn("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)) {
+                logger.debug("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) {
+            logger.error(ce, ce);
+        }
+
+        logger.warn("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/GetArtifactServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,86 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(GetArtifactServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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) {
+            logger.error(ce, ce);
+        }
+
+        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/LoadArtifactServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,147 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+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.shared.model.Recommendation;
+
+import de.intevation.flys.client.client.services.LoadArtifactService;
+
+/**
+ * This service creates a new Artifact based on a given Recommendation and puts
+ * this new artifact into a specified Collection.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LoadArtifactServiceImpl
+extends      ArtifactServiceImpl
+implements   LoadArtifactService
+{
+    private static final Logger logger =
+        Logger.getLogger(LoadArtifactServiceImpl.class);
+
+
+    public static final String ERROR_LOAD_ARTIFACT = "error_load_artifact";
+
+
+    /**
+     * Clones or creates a single artifact and adds it to a collection.
+     *
+     * Note that in contrast to loadMany, always the given factory is used
+     * to clone the artifact.
+     *
+     * @param parent  collection to add recommendation to.
+     * @param recom   recommendation to create clone for.
+     * @param factory factory to use.
+     * @param url     servers url.
+     * @param locale  the locale to translate messages.
+     */
+    public Artifact load(
+        Collection     parent,
+        Recommendation recom,
+        String         factory,
+        String         url,
+        String         locale
+    )
+    throws ServerException {
+        logger.info(
+            "LoadArtifactServiceImpl.load: " + recom.getMasterArtifact());
+
+        // 1) Clone the Artifact specified in >>recom<<
+        Artifact clone = ArtifactHelper.createArtifact(
+            url, locale, factory, recom);
+
+        if (clone != null) {
+            logger.debug("Successfully create Artifact Clone. Add now!");
+            Collection c = CollectionHelper.addArtifact(
+                parent, clone, url, locale);
+
+            if (c != null) {
+                logger.debug("Successfully added Clone to Collection.");
+
+                return clone;
+            }
+        }
+
+        throw new ServerException(ERROR_LOAD_ARTIFACT);
+    }
+
+
+    /**
+     * Clone/create one or more artifacts and add it to a collection, avoiding
+     * duplicates.
+     *
+     * @param parent  Collection where clones will be added to.
+     * @param recoms  definitions of source of clone.
+     * @param factory name of factory to use when cloning artifacts (can be
+     *                null in which case the recommendations getFactory() will
+     *                be used.
+     * @param url     servers url.
+     * @param locale  the locale to translate messages.
+     *
+     * @return cloned artifacts (same artifact might be contained multiple
+     *         times).
+     */
+    public Artifact[] loadMany(
+        Collection       parent,
+        Recommendation[] recoms,
+        String           factory,
+        String           url,
+        String           locale
+    )
+    throws ServerException {
+        logger.debug("LoadArtifactServiceImpl.loadMany");
+
+        ArrayList<Artifact> artifacts = new ArrayList<Artifact>();
+        HashMap<Recommendation, Artifact> cloneMap =
+            new HashMap<Recommendation, Artifact>();
+
+        // TODO Respect the index of what to clone.
+
+        // 1) Clone the Artifacts specified in >>recoms<<
+        for (Recommendation recom : recoms) {
+            // Do not do two clones of two identical recommendations.
+            Artifact prevClone = cloneMap.get(recom);
+            if (prevClone != null) {
+                // Already cloned a recommendation like this.
+                logger.debug("LoadArtifactServiceImpl: Avoid reclones, "
+                    + "clone already exists.");
+                artifacts.add(prevClone);
+            }
+            else {
+                // Not already cloned.
+                String realFactory = factory != null
+                    ? factory
+                    : recom.getFactory();
+
+                logger.debug("One will be cloned with : " + realFactory);
+
+                Artifact clone = ArtifactHelper.createArtifact(
+                    url, locale, realFactory, recom);
+        
+                if (clone != null) {
+                    logger.debug("LoadArtifactServiceImple: Successfully "
+                        + "loaded Artifact Clone.");
+                    Collection c = CollectionHelper.addArtifact(
+                        parent, clone, url, locale);
+        
+                    if (c != null) {
+                        artifacts.add(clone);
+                        // Remember we cloned a recommendation like this.
+                        cloneMap.put(recom, clone);
+                    }
+                    else {
+                        throw new ServerException(ERROR_LOAD_ARTIFACT);
+                    }
+                }
+            }
+        }
+        return artifacts.toArray(new Artifact[artifacts.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/MapHelper.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,89 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.client.shared.model.MapConfig;
+
+
+public class MapHelper {
+
+    private static final Logger logger = Logger.getLogger(MapHelper.class);
+
+
+    public static final String XPATH_SRID =
+        "/art:floodmap/art:srid/text()";
+
+    public static final String XPATH_MAX_EXTENT =
+        "/art:floodmap/art:maxExtent/text()";
+
+    public static final String XPATH_INITIAL_EXTENT =
+        "/art:floodmap/art:initialExtent/text()";
+
+
+    private MapHelper() {
+    }
+
+
+    public static MapConfig parseConfig(Document raw) {
+        logger.debug("MapHelper.parseConfig");
+
+        logger.debug(XMLUtils.toString(raw));
+
+        MapConfig config = new MapConfig();
+
+        setSrid(config, raw);
+        setMaxExtent(config, raw);
+        setInitialExtent(config, raw);
+
+        return config;
+    }
+
+
+    protected static void setSrid(MapConfig config, Document raw) {
+        String srid = (String) XMLUtils.xpathString(
+            raw,
+            XPATH_SRID,
+            ArtifactNamespaceContext.INSTANCE);
+
+        logger.debug("Found srid: '" + srid + "'");
+
+        if (srid != null && srid.length() > 0) {
+            logger.debug("Set srid: '" + srid + "'");
+            config.setSrid(srid);
+        }
+    }
+
+
+    protected static void setMaxExtent(MapConfig config, Document raw) {
+        String maxExtent = (String) XMLUtils.xpathString(
+            raw,
+            XPATH_MAX_EXTENT,
+            ArtifactNamespaceContext.INSTANCE);
+
+        logger.debug("Found max extent: '" + maxExtent + "'");
+
+        if (maxExtent != null && maxExtent.length() > 0) {
+            logger.debug("Set max extent: '" + maxExtent + "'");
+            config.setMaxExtent(maxExtent);
+        }
+    }
+
+
+    protected static void setInitialExtent(MapConfig config, Document raw) {
+        String initialExtent = (String) XMLUtils.xpathString(
+            raw,
+            XPATH_INITIAL_EXTENT,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (initialExtent != null && initialExtent.length() > 0) {
+            logger.debug("Set initial extent: '" + initialExtent + "'");
+            config.setInitialExtent(initialExtent);
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/MapInfoServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,130 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+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.BBox;
+import de.intevation.flys.client.shared.model.MapInfo;
+
+import de.intevation.flys.client.client.services.MapInfoService;
+
+
+/**
+ * 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 MapInfoServiceImpl
+extends      RemoteServiceServlet
+implements   MapInfoService
+{
+    private static final Logger logger =
+        Logger.getLogger(MapInfoServiceImpl.class);
+
+
+    public static final String XPATH_RIVER =
+        "/mapinfo/river/@name";
+
+    public static final String XPATH_SRID =
+        "/mapinfo/river/srid/@value";
+
+    public static final String XPATH_BBOX  =
+        "/mapinfo/river/bbox/@value";
+
+    public static final String XPATH_RIVER_WMS =
+        "/mapinfo/river/river-wms/@url";
+
+    public static final String XPATH_WMS_URL =
+        "/mapinfo/river/background-wms/@url";
+
+    public static final String XPATH_WMS_LAYERS =
+        "/mapinfo/river/background-wms/@layers";
+
+    public static final String ERROR_NO_MAPINFO_FOUND =
+        "mapinfo_service_no_result";
+
+
+    public MapInfo getMapInfo(String url, String locale, String river)
+    throws ServerException
+    {
+        logger.info("MapInfoServiceImpl.getMapInfo");
+
+        Document request = getRequestDocument(river);;
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            logger.debug("MapInfoServiceImpl.callService");
+            Document result = client.callService(url, "mapinfo", request);
+
+            if (result == null) {
+                logger.warn("MapInfo service returned no result.");
+                throw new ServerException(ERROR_NO_MAPINFO_FOUND);
+            }
+
+            return getMapInfo(result);
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        throw new ServerException(ERROR_NO_MAPINFO_FOUND);
+    }
+
+
+    public static Document getRequestDocument(String rivername) {
+        logger.debug("MapInfoServiceImpl.getRequestDocument");
+
+        Document  request = XMLUtils.newDocument();
+        ElementCreator cr = new ElementCreator(request, null, null);
+
+        Element root  = cr.create("mapinfo");
+        Element river = cr.create("river");
+
+        river.setTextContent(rivername);
+
+        request.appendChild(root);
+        root.appendChild(river);
+
+        return request;
+    }
+
+
+    public static MapInfo getMapInfo(Document result) {
+        logger.debug("MapInfoServiceImpl.getMapInfo");
+
+        String river   = XMLUtils.xpathString(result, XPATH_RIVER, null);
+        String sridStr = XMLUtils.xpathString(result, XPATH_SRID, null);
+        String bboxS   = XMLUtils.xpathString(result, XPATH_BBOX,  null);
+        BBox   bbox    = BBox.getBBoxFromString(bboxS);
+
+        String riverWMS  = XMLUtils.xpathString(result, XPATH_RIVER_WMS, null);
+        String wmsURL    = XMLUtils.xpathString(result, XPATH_WMS_URL, null);
+        String wmsLayers = XMLUtils.xpathString(result, XPATH_WMS_LAYERS, null);
+
+        int srid = 4326;
+
+        try {
+            srid = Integer.parseInt(sridStr);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        return new MapInfo(river, srid, bbox, riverWMS, wmsURL, wmsLayers);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/MapOutputServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,67 @@
+package de.intevation.flys.client.server;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+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.artifacts.httpclient.exceptions.ConnectionException;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.MapConfig;
+
+import de.intevation.flys.client.client.services.MapOutputService;
+
+
+public class MapOutputServiceImpl
+extends      RemoteServiceServlet
+implements   MapOutputService
+{
+
+    private static final Logger logger =
+        Logger.getLogger(MapOutputServiceImpl.class);
+
+
+    public static final String ERROR_NO_MAP_CONFIG = "error_no_map_config";
+
+
+    public MapConfig doOut(Collection collection)
+    throws ServerException
+    {
+        logger.info("MapOutputServiceImpl.doOut");
+
+        String url  = getServletContext().getInitParameter("server-url");
+        String uuid = collection.identifier();
+
+        try {
+            Document request = ClientProtocolUtils.newOutCollectionDocument(
+                uuid, "floodmap", "floodmap");
+
+            HttpClient client = new HttpClientImpl(url);
+            InputStream    is = client.collectionOut(request, uuid, "floodmap");
+
+            Document response = XMLUtils.parseDocument(is);
+
+            return MapHelper.parseConfig(response);
+        }
+        catch (ConnectionException e) {
+            logger.error(e, e);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+
+        throw new ServerException(ERROR_NO_MAP_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-client/src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,98 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(MetaDataServiceImpl.class);
+
+
+    public static final String ERROR_NO_META_DATA_FOUND =
+        "error_no_meta_data_found";
+
+    @Override
+    public DataCageTree getMetaData(
+        String url,
+        String locale,
+        String artifactId,
+        String userId,
+        String outs,
+        String parameters
+    ) throws ServerException
+    {
+        logger.info("MetaDataService.getMetaData");
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element meta = ec.create("meta");
+
+        if (artifactId != null) {
+            Element artifactEl = ec.create("artifact-id");
+            artifactEl.setAttribute("value", artifactId);
+            meta.appendChild(artifactEl);
+        }
+
+        if (userId != null) {
+            Element userEl = ec.create("user-id");
+            userEl.setAttribute("value", userId);
+            meta.appendChild(userEl);
+        }
+
+        if (outs != null) {
+            Element outsEl = ec.create("outs");
+            outsEl.setAttribute("value", outs);
+            meta.appendChild(outsEl);
+        }
+
+        if (parameters != null) {
+            Element paramsEl = ec.create("parameters");
+            paramsEl.setAttribute("value", parameters);
+            meta.appendChild(paramsEl);
+        }
+
+        doc.appendChild(meta);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Converter converter = new Converter();
+            return converter.convert(client.callService(url, "metadata", doc));
+        }
+        catch (ConnectionException ce) {
+            ce.printStackTrace();
+        }
+
+        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/RemoveArtifactServiceImpl.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,33 @@
+package de.intevation.flys.client.server;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.client.services.RemoveArtifactService;
+
+
+/**
+ * Implementation of RemoveArtifactService .
+ */
+public class RemoveArtifactServiceImpl
+extends      DescribeCollectionServiceImpl
+implements   RemoveArtifactService
+{
+    private static final Logger logger =
+        Logger.getLogger(RemoveArtifactServiceImpl.class);
+
+
+    public Collection remove(
+        Collection collection,
+        String     artifactId,
+        String     url,
+        String     locale)
+    throws ServerException
+    {
+        logger.info("RemoveArtifactServiceImpl.remove");
+
+        return CollectionHelper.removeArtifact(collection, artifactId, url, locale);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,115 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(ReportServiceImpl.class);
+
+
+    @Override
+    public String report(
+        String collectionId,
+        String url,
+        String locale,
+        String out
+    ) {
+        logger.info("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) {
+                logger.debug("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:51 2012 +0200
@@ -0,0 +1,100 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(RiverServiceImpl.class);
+
+
+    /** 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) {
+            logger.error(ce, ce);
+        }
+
+        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:51 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(SetCollectionNameServiceImpl.class);
+
+
+    public void setName(Collection c, String url)
+    throws ServerException
+    {
+        logger.info("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:51 2012 +0200
@@ -0,0 +1,51 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(SetCollectionTTLServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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:51 2012 +0200
@@ -0,0 +1,178 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(StepForwardServiceImpl.class);
+
+
+    /** 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
+    {
+        logger.info("StepForwardServiceImpl.go");
+
+        Artifact afterFeed = feed(url, locale, artifact, data);
+
+        if (afterFeed == null) {
+            logger.warn("StepForwardService.feed() - FAILED");
+            throw new ServerException(ERROR_FEED_DATA);
+        }
+
+        ArtifactDescription desc = afterFeed.getArtifactDescription();
+        String[] reachable       = desc.getReachableStates();
+
+        if (reachable == null || reachable.length == 0) {
+            logger.debug("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
+    {
+        logger.info("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) {
+                logger.warn("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)) {
+                logger.debug("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) {
+            logger.error(ce, ce);
+        }
+
+        logger.warn("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:51 2012 +0200
@@ -0,0 +1,124 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger = Logger.getLogger(
+        UserCollectionsServiceImpl.class);
+
+
+    public Collection[] getUserCollections(
+        String serverUrl,
+        String locale,
+        String userid)
+    {
+        logger.info("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) {
+                logger.debug("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);
+                }
+            }
+
+            logger.debug("User has " + all.size() + " collections.");
+
+            return (Collection[]) all.toArray(new Collection[all.size()]);
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        logger.debug("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) {
+                logger.warn("Error while parsing collection attributes.");
+                return null;
+            }
+        }
+
+        logger.warn("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:51 2012 +0200
@@ -0,0 +1,73 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger = Logger.getLogger(UserServiceImpl.class);
+
+
+    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) {
+            logger.error(ce, ce);
+        }
+
+        logger.error("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:51 2012 +0200
@@ -0,0 +1,178 @@
+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 org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger =
+        Logger.getLogger(WQInfoServiceImpl.class);
+
+
+    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
+    {
+        logger.info("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);
+
+            logger.debug("Extract wq info objects now.");
+            WQInfoObject[] objects = extractWQInfoObjects(result);
+
+            if (objects != null && objects.length > 0) {
+                return objects;
+            }
+        }
+        catch (ConnectionException ce) {
+            logger.error(ce, ce);
+        }
+
+        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) {
+            logger.warn("No wq info found.");
+
+            throw new ServerException(ERROR_NO_WQINFO_FOUND);
+        }
+
+        int num = list.getLength();
+        logger.debug("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);
+            }
+        }
+
+        logger.debug("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) {
+                logger.warn(nfe.getLocalizedMessage());
+            }
+        }
+
+        logger.warn("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:51 2012 +0200
@@ -0,0 +1,144 @@
+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 org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.client.shared.model.DataCageTree;
+import de.intevation.flys.client.shared.model.DataCageNode;
+import de.intevation.flys.client.shared.model.AttrList;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+public class Converter
+{
+    private static final Logger logger = Logger.getLogger(Converter.class);
+
+
+    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"),
+                toAttrList(node.getAttributes()));
+            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");
+
+            String name = node.getLocalName();
+            String desc = node.hasAttribute("description")
+                ? node.getAttribute("description")
+                : "${" + name + "}";
+
+            DataCageNode out =
+                new DataCageNode(name, desc, toAttrList(node.getAttributes()));
+
+            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) {
+                    converter = I18N_CONVERTER;
+                }
+                DataCageNode son = converter.convert(
+                    (Element)child, this);
+                parent.addChild(son);
+            }
+        } // for all children
+    }
+
+    public Converter() {
+        converters = new HashMap<String, NodeConverter>();
+        registerConverters();
+    }
+
+    public static final NodeConverter NAME_CONVERTER = new NameConverter(); 
+    public static final NodeConverter I18N_CONVERTER = new I18NConverter(); 
+
+    protected void registerConverters() {
+        logger.debug("register converters called");
+        converters.put("river",      NAME_CONVERTER);
+        converters.put("gauge",      NAME_CONVERTER);
+        converters.put("historical", NAME_CONVERTER);
+        converters.put("column",     NAME_CONVERTER);
+    }
+
+    public DataCageTree convert(Document document) {
+        logger.debug("convert called");
+        //System.err.println(XMLUtils.toString(document));
+
+        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) {
+                logger.debug("search for name: " + node.getLocalName());
+                NodeConverter converter = converters.get(node.getLocalName());
+                if (converter == null) {
+                    converter = I18N_CONVERTER;
+                }
+                roots.add(converter.convert((Element)node, this));
+            }
+        }
+        return roots.isEmpty()
+            ? new DataCageTree()
+            : new DataCageTree(roots.get(0));
+    }
+
+    public static AttrList toAttrList(NamedNodeMap nodeMap) {
+        if (nodeMap == null) {
+            return null;
+        }
+        int N = nodeMap.getLength();
+
+        if (N == 0) {
+            return null;
+        }
+
+        AttrList result = new AttrList(N);
+
+        for (int i = 0; i < N; ++i) {
+            Node node = nodeMap.item(i);
+            if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+                Attr   attr  = (Attr)node;
+                String key   = attr.getName();
+                String value = attr.getValue();
+                result.add(key, value);
+            }
+        }
+
+        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-client/src/main/java/de/intevation/flys/client/shared/DoubleUtils.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,54 @@
+package de.intevation.flys.client.shared;
+
+
+public final class DoubleUtils {
+
+    public static final String DEFAULT_DELIM = " ";
+
+
+    private DoubleUtils() {
+    }
+
+
+    public static Double getDouble(String value) {
+        try {
+            return Double.valueOf(value);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        return null;
+    }
+
+
+    public static double[] getMinMax(String value) {
+        return getMinMax(value, DEFAULT_DELIM);
+    }
+
+
+    public static double[] getMinMax(String value, String delim) {
+        if (value == null) {
+            return null;
+        }
+
+        String[] values = value.split(delim);
+
+        int     len = values != null ? values.length : 0;
+        double[] mm = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE };
+
+        for (int i = 0; i < len; i++) {
+            Double d = getDouble(values[i]);
+
+            if (d != null) {
+                mm[0] = mm[0] < d ? mm[0] : d;
+                mm[1] = mm[1] > d ? mm[1] : d;
+            }
+        }
+
+        return mm[0] != Double.MAX_VALUE && mm[1] != -Double.MAX_VALUE
+            ? mm
+            : 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/FieldVerifier.java	Fri Sep 28 12:14:51 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/MapUtils.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.shared;
+
+
+public class MapUtils {
+
+    public static final String GET_LEGEND_GRAPHIC_TEMPLATE =
+        "${SERVER}SERVICE=WMS&VERSION=1.1.1&layer=${LAYER}" +
+        "&REQUEST=getLegendGraphic&FORMAT=image/png";
+
+
+    private MapUtils() {
+    }
+
+
+    public static String getLegendGraphicUrl(String server, String layer) {
+        if (server == null || layer == null) {
+            return null;
+        }
+
+        server = server.indexOf("?") >= 0 ? server : server + "?";
+
+        String url = GET_LEGEND_GRAPHIC_TEMPLATE;
+        url = url.replace("${SERVER}", server);
+        url = url.replace("${LAYER}", layer);
+
+        return 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/shared/Transform2D.java	Fri Sep 28 12:14:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,69 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 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();
+
+
+    /**
+     * Returns true, if the Artifact is in Background mode.
+     *
+     * @return true, if the artifact is in background mode.
+     */
+    public boolean isInBackground();
+
+
+    /**
+     * Return a list of background messages.
+     *
+     * @return a list of background messages.
+     */
+    public List<CalculationMessage> getBackgroundMessages();
+
+
+    /**
+     * 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:51 2012 +0200
@@ -0,0 +1,80 @@
+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 name of the selected river.
+     *
+     * @return the selected river.
+     */
+    public String getRiver();
+
+
+    /**
+     * Returns the selected min and max kilomter if existing otherwise null.
+     *
+     * @return an array of [min-km, max-km] if existing otherwise null.
+     */
+    public double[] getKMRange();
+
+
+    /**
+     * Returns the available output modes.
+     *
+     * @return the available output modes.
+     */
+    public OutputMode[] getOutputModes();
+
+
+    /**
+     * Returns the recommended artifacts suggested by the server.
+     *
+     * @return the recommended artifacts.
+     */
+    public Recommendation[] getRecommendations();
+}
+// vim:set ts=4 sw=4 si et sta 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/ArtifactFilter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,65 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+
+public class ArtifactFilter
+implements   Serializable
+{
+    protected String factoryName;
+
+    protected Map<String, OutFilter> outFilters;
+
+    public ArtifactFilter() {
+        outFilters = new HashMap<String, OutFilter>();
+    }
+
+    public ArtifactFilter(String factoryName) {
+        this();
+        this.factoryName = factoryName;
+    }
+
+    public String getFactoryName() {
+        return factoryName;
+    }
+
+    public void setFactoryName(String factoryName) {
+        this.factoryName = factoryName;
+    }
+
+    public void add(String out, String name, String num) {
+
+        if (out == null) {
+            out = ToLoad.uniqueKey(outFilters);
+        }
+
+        OutFilter outFilter = outFilters.get(out);
+
+        if (outFilter == null) {
+            outFilter = new OutFilter(out);
+            outFilters.put(out, outFilter);
+        }
+        outFilter.add(name, num);
+    }
+
+    public String collectIds() {
+        StringBuilder sb = new StringBuilder();
+        for (OutFilter outFilter: outFilters.values()) {
+            outFilter.collectIds(sb);
+        }
+        return sb.toString();
+    }
+
+    public Recommendation.Filter toFilter() {
+        Recommendation.Filter rf = new Recommendation.Filter();
+        for (Map.Entry<String, OutFilter> entry: outFilters.entrySet()) {
+            List<Recommendation.Facet> facets = entry.getValue().toFacets();
+            rf.add(entry.getKey(), facets);
+        }
+        return rf;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/AttrList.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,37 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.Serializable;
+
+public class AttrList implements Serializable
+{
+    protected List<String> keyValues;
+
+    public AttrList() {
+        this(5);
+    }
+
+    public AttrList(int size) {
+        keyValues = new ArrayList<String>(size*2);
+    }
+
+    public int size() {
+        return keyValues != null ? keyValues.size()/2 : null;
+    }
+
+    public String getKey(int index) {
+        return keyValues.get(index*2);
+    }
+
+    public String getValue(int index) {
+        return keyValues.get(index*2 + 1);
+    }
+
+    public void add(String key, String value) {
+        keyValues.add(key);
+        keyValues.add(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/AttributedTheme.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,181 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AttributedTheme implements Theme {
+
+    protected Map<String, String> attributes;
+
+
+    public AttributedTheme() {
+        this.attributes = new HashMap<String, String>();
+    }
+
+
+    public Set<String> getKeys() {
+        return attributes.keySet();
+    }
+
+
+    public void addAttr(String name, String value) {
+        if (name != null && value != null) {
+            attributes.put(name, value);
+        }
+    }
+
+
+    public String getAttr(String name) {
+        return attributes.get(name);
+    }
+
+
+    public Integer getAttrAsInt(String name) {
+        String attr = getAttr(name);
+
+        if (attr != null && attr.length() > 0) {
+            try {
+                return Integer.parseInt(attr);
+            }
+            catch (NumberFormatException nfe) {
+            }
+        }
+
+        return null;
+    }
+
+
+    public boolean getAttrAsBoolean(String name) {
+        String attr = getAttr(name);
+
+        if (attr != null) {
+            try {
+                int num = Integer.valueOf(attr);
+                return num > 0;
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+
+        return Boolean.valueOf(attr);
+    }
+
+
+    @Override
+    public int getPosition() {
+        Integer pos = getAttrAsInt("pos");
+
+        return pos != null ? pos.intValue() : -1;
+    }
+
+
+    @Override
+    public void setPosition(int pos) {
+        addAttr("pos", String.valueOf(pos));
+    }
+
+
+    @Override
+    public int getIndex() {
+        Integer idx = getAttrAsInt("index");
+
+        return idx != null ? idx.intValue() : -1;
+    }
+
+
+    @Override
+    public int getActive() {
+        return getAttrAsInt("active");
+    }
+
+
+    @Override
+    public void setActive(int active) {
+        addAttr("active", String.valueOf(active));
+    }
+
+
+    @Override
+    public String getArtifact() {
+        return getAttr("artifact");
+    }
+
+
+    @Override
+    public String getFacet() {
+        return getAttr("facet");
+    }
+
+
+    @Override
+    public String getDescription() {
+        return getAttr("description");
+    }
+
+
+    @Override
+    public void setDescription(String description) {
+        if (description != null && description.length() > 0) {
+            addAttr("description", description);
+        }
+    }
+
+
+    @Override
+    public int getVisible() {
+        return getAttrAsInt("visible");
+    }
+
+
+    @Override
+    public void setVisible(int visible) {
+        addAttr("visible", String.valueOf(visible));
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof AttributedTheme)) {
+            return false;
+        }
+
+        AttributedTheme other = (AttributedTheme) o;
+
+        if (other.getPosition() != getPosition()) {
+            return false;
+        }
+
+        if (!other.getArtifact().equals(getArtifact())) {
+            return false;
+        }
+
+        if (other.getActive() != getActive()) {
+            return false;
+        }
+
+        if (!other.getFacet().equals(getFacet())) {
+            return false;
+        }
+
+        if (!other.getDescription().equals(getDescription())) {
+            return false;
+        }
+
+        if (other.getIndex() != getIndex()) {
+            return false;
+        }
+
+        if (other.getVisible() != getVisible()) {
+            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/Axis.java	Fri Sep 28 12:14:51 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/BBox.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,74 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+public class BBox implements Serializable {
+
+    public double lowerX;
+    public double upperX;
+    public double lowerY;
+    public double upperY;
+
+
+    public BBox() {
+    }
+
+
+    public BBox(double lowerX, double lowerY, double upperX, double upperY) {
+        this.lowerX = lowerX;
+        this.lowerY = lowerY;
+        this.upperX = upperX;
+        this.upperY = upperY;
+    }
+
+
+    public double getLowerX() {
+        return lowerX;
+    }
+
+
+    public double getLowerY() {
+        return lowerY;
+    }
+
+
+    public double getUpperX() {
+        return upperX;
+    }
+
+
+    public double getUpperY() {
+        return upperY;
+    }
+
+
+    public String toString() {
+        return
+            "(" + lowerX + "," + lowerY + ")" +
+            "(" + upperX + "," + upperY + ")";
+    }
+
+
+    public static BBox getBBoxFromString(String bbox) {
+        String[] coords = bbox != null ? bbox.split(" ") : null;
+
+        if (coords == null || coords.length < 4) {
+            return null;
+        }
+
+        try {
+            return new BBox(
+                Double.parseDouble(coords[0]),
+                Double.parseDouble(coords[1]),
+                Double.parseDouble(coords[2]),
+                Double.parseDouble(coords[3]));
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing here
+        }
+
+        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/CalculationMessage.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,37 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+public class CalculationMessage implements Serializable {
+
+    protected int    steps;
+    protected int    currentStep;
+    protected String message;
+
+
+    public CalculationMessage() {
+    }
+
+
+    public CalculationMessage(int steps, int currentStep, String message) {
+        this.steps       = steps;
+        this.currentStep = currentStep;
+        this.message     = message;
+    }
+
+
+    public int getSteps() {
+        return steps;
+    }
+
+
+    public int getCurrentStep() {
+        return currentStep;
+    }
+
+
+    public String getMessage() {
+        return message;
+    }
+}
--- /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:51 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/ChartMode.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,39 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.OutputTab;
+import de.intevation.flys.client.client.ui.chart.ChartOutputTab;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartMode extends DefaultOutputMode {
+
+    public ChartMode() {
+    }
+
+
+    public ChartMode(String name, String desc, String mimeType) {
+        super(name, desc, mimeType);
+    }
+
+
+    public ChartMode(
+        String name,
+        String descrition,
+        String mimeType,
+        List<Facet> facets)
+    {
+        super(name, descrition, mimeType, facets);
+    }
+
+
+    @Override
+    public OutputTab createOutputTab(String t, Collection c, CollectionView p) {
+        return new ChartOutputTab(t, c, this, p);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,48 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+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);
+
+    public List<Recommendation> getRecommendations();
+
+    public void addRecommendation(Recommendation recommendation);
+
+    public void addRecommendations(List<Recommendation> recommendations);
+
+    public boolean loadedRecommendation(Recommendation recommendation);
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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/CollectionItemAttribute.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,106 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+import java.util.List;
+import java.util.ArrayList;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class CollectionItemAttribute implements Serializable {
+
+    /** The list of styles. */
+    protected List<Style> styles;
+
+    /** The artifact. */
+    protected String artifact;
+
+
+    /**
+     * Creates a new CollectionItem Attribute.
+     */
+    public CollectionItemAttribute() {
+        styles = new ArrayList<Style>();
+    }
+
+
+    /**
+     * Append a new Style.
+     * @param style The style.
+     */
+    public void appendStyle (Style style) {
+        this.styles.add(style);
+    }
+
+
+    /**
+     * Remove a style from the attributes.
+     * @param name The style name.
+     */
+    public void removeStyle (String name) {
+        for (int i = 0; i < styles.size(); i++) {
+            if (styles.get(i).getName().equals(name)) {
+                styles.remove(i);
+            }
+        }
+    }
+
+
+    /**
+     * Get a style from the collection item.
+     * @param facet The facet this style belongs to.
+     * @param index The style index.
+     *
+     * @return The selected style or 'null'.
+     */
+    public Style getStyle(String facet, int index) {
+        for (int i = 0; i < styles.size(); i++) {
+            Style tmp = styles.get(i);
+            if (tmp.getFacet().equals(facet) &&
+                tmp.getIndex() == index) {
+                return tmp;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Get the style at a postion.
+     * @param i The position index.
+     *
+     * @return The selected style.
+     */
+    public Style getStyle(int i) {
+        return styles.get(i);
+    }
+
+
+    /**
+     * Get the number of styles.
+     * @return The number of styles.
+     */
+    public int getNumStyles() {
+        return styles.size();
+    }
+
+
+    /**
+     * Set the current artifact.
+     * @param The artifact uuid.
+     */
+    public void setArtifact(String artifact) {
+        this.artifact = artifact;
+    }
+
+
+    /**
+     * Get the associated artifact.
+     * @return The artifact.
+     */
+    public String getArtifact () {
+        return this.artifact;
+    }
+}
--- /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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,59 @@
+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 String             description;
+    protected List<DataCageNode> children;
+    protected AttrList           attrs;
+
+    public DataCageNode() {
+    }
+
+    public DataCageNode(String name) {
+        this(name, null);
+    }
+
+    public DataCageNode(String name, AttrList attrs) {
+        this(name, name, attrs);
+    }
+
+    public DataCageNode(String name, String description, AttrList attrs) {
+        this.name        = name;
+        this.description = description;
+        this.attrs       = attrs;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    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;
+    }
+
+    public AttrList getAttributes() {
+        return attrs;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,177 @@
+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, Cloneable {
+
+    /** 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;
+    }
+
+
+    public Object clone() {
+        DataList clone = new DataList(
+            this.state,
+            this.data.size(),
+            this.uiprovider,
+            this.label);
+        clone.data = (List<Data>) ((ArrayList<Data>)data).clone();
+
+        return clone;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,94 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+
+/**
+ * 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;
+
+    /** Determines if the artifact is in background mode.*/
+    protected boolean inBackground;
+
+    protected List<CalculationMessage> messages;
+
+    /** 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() {
+    }
+
+
+    public DefaultArtifact(String uuid, String hash) {
+        this(uuid, hash, false, null);
+    }
+
+
+    /**
+     * 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,
+        boolean                  inBackground,
+        List<CalculationMessage> messages
+    ) {
+        this.uuid         = uuid;
+        this.hash         = hash;
+        this.inBackground = inBackground;
+        this.messages     = messages;
+    }
+
+
+    public String getUuid() {
+        return uuid;
+    }
+
+
+    public String getHash() {
+        return hash;
+    }
+
+
+    public String getName() {
+        return "default";
+    }
+
+
+    public boolean isInBackground() {
+        return inBackground;
+    }
+
+
+    public List<CalculationMessage> getBackgroundMessages() {
+        return messages;
+    }
+
+
+    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:51 2012 +0200
@@ -0,0 +1,169 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+import de.intevation.flys.client.shared.DoubleUtils;
+
+
+/**
+ * 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;
+
+    /** A list of recommendations suggested by the server.*/
+    protected Recommendation[] recommendations;
+
+
+    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,
+        Recommendation[] recommendations)
+    {
+        this(old, current, state, reachableStates);
+        this.outputModes     = outputModes;
+        this.recommendations = recommendations;
+    }
+
+
+    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;
+    }
+
+
+    public Recommendation[] getRecommendations() {
+        return recommendations;
+    }
+
+
+    public String getRiver() {
+        for (DataList list: oldData) {
+            List<Data> dataList = list.getAll();
+
+            for (Data d: dataList) {
+                String dataName = d.getLabel();
+                DataItem item   = d.getItems()[0];
+
+                if (dataName.equals("river")) {
+                    return item.getStringValue();
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    public double[] getKMRange() {
+        Double[] mm = new Double[2];
+
+        for (DataList list: oldData) {
+            List<Data> dataList = list.getAll();
+
+            for (Data data: dataList) {
+                String dataName = data.getLabel();
+                DataItem item   = data.getItems()[0];
+
+                if (dataName.equals("ld_from")) {
+                    Double d = DoubleUtils.getDouble(item.getStringValue());
+
+                    if (d != null) {
+                        mm[0] = d;
+                    }
+                }
+                else if (dataName.equals("ld_to")) {
+                    Double d = DoubleUtils.getDouble(item.getStringValue());
+
+                    if (d != null) {
+                        mm[1] = d;
+                    }
+                }
+                else if (dataName.equals("ld_locations")) {
+                    return DoubleUtils.getMinMax(item.getStringValue());
+                }
+            }
+
+            if (mm[0] != null && mm[1] != null) {
+                return new double[] { mm[0], mm[1] };
+            }
+        }
+
+        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/DefaultCollection.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,215 @@
+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 List<Recommendation> recommendations;
+
+    protected Map<String, ThemeList> themeLists;
+
+
+    /**
+     * Constructor without arguments is necessary for GWT.
+     */
+    public DefaultCollection() {
+    }
+
+
+    public DefaultCollection(String uuid, long ttl, String name) {
+        this.uuid       = uuid;
+        this.ttl        = ttl;
+        this.name       = name;
+        this.items      = new ArrayList<CollectionItem>();
+        this.themeLists = new HashMap<String, ThemeList>();
+        this.recommendations = new ArrayList<Recommendation>();
+    }
+
+
+    /**
+     * Creates a new DefaultCollection with a UUID.
+     *
+     * @param uuid The UUID.
+     */
+    public DefaultCollection(
+        String uuid,
+        long   ttl,
+        String name,
+        List<Recommendation> recs
+    ) {
+        this(uuid, ttl, name);
+
+        this.recommendations = recs;
+    }
+
+
+    public DefaultCollection(
+        String uuid,
+        long   ttl,
+        String name,
+        List<Recommendation> recommendations,
+        Map<String, ThemeList> themeLists)
+    {
+        this(uuid, ttl, name, recommendations);
+        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.
+     * @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;
+    }
+
+
+    public List<Recommendation> getRecommendations() {
+        return recommendations;
+    }
+
+
+    public void addRecommendation(Recommendation recommendation) {
+        recommendations.add(recommendation);
+    }
+
+
+    public void addRecommendations(List<Recommendation> recommendations) {
+        this.recommendations.addAll(recommendations);
+    }
+
+
+    public boolean loadedRecommendation(Recommendation recommendation) {
+        String factory = recommendation.getFactory();
+        String dbids   = recommendation.getIDs();
+
+        for (Recommendation in: recommendations) {
+            String inFactory = in.getFactory();
+            String inDbids   = in.getIDs();
+
+            if (factory.equals(inFactory) && dbids.equals(inDbids)) {
+                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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultCollectionItem.java	Fri Sep 28 12:14:51 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:51 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:51 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:51 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:51 2012 +0200
@@ -0,0 +1,153 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.OutputTab;
+
+
+/**
+ * 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 type that this output mode represents.*/
+    protected String type;
+
+    /** 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,
+        String      type)
+    {
+        this(name, description, mimeType);
+
+        this.type = type;
+    }
+
+
+    public DefaultOutputMode(
+        String name,
+        String description,
+        String mimeType,
+        List<Facet> facets)
+    {
+        this(name, description, mimeType);
+        this.type   = "";
+        this.facets = facets;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    public OutputTab createOutputTab(String t, Collection c, CollectionView p) {
+        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/DefaultRiver.java	Fri Sep 28 12:14:51 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:51 2012 +0200
@@ -0,0 +1,140 @@
+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 int active;
+
+    protected int visible;
+
+    protected String artifact;
+
+    protected String facet;
+
+    protected String description;
+
+
+    public DefaultTheme() {
+    }
+
+
+    public DefaultTheme(
+        int     pos,
+        int     index,
+        int     active,
+        int     visible,
+        String  art,
+        String  facet,
+        String  description)
+    {
+        this.position    = pos;
+        this.index       = index;
+        this.active      = active;
+        this.visible     = visible;
+        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 int getActive() {
+        return active;
+    }
+
+
+    public void setActive(int active) {
+        this.active = active;
+    }
+
+
+    public String getArtifact() {
+        return artifact;
+    }
+
+
+    public String getFacet() {
+        return facet;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    public int getVisible() {
+        return visible;
+    }
+
+
+    public void setVisible(int visible) {
+        this.visible = visible;
+    }
+
+
+    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;
+        }
+
+        if (other.visible != visible) {
+            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:51 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:51 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:51 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:51 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:51 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:51 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/FacetFilter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,63 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+public class FacetFilter
+implements   Serializable
+{
+    protected String name;
+    protected String ids;
+
+    public FacetFilter() {
+    }
+
+    public FacetFilter(String name, String ids) {
+        this.name = name;
+        this.ids  = ids;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getIds() {
+        return ids;
+    }
+
+    public void setIds(String ids) {
+        this.ids = ids;
+    }
+
+    protected static boolean equals(String a, String b) {
+        if (a == null && b == null) return true;
+        if (a == null && b != null) return false;
+        if (a != null && b == null) return false;
+        return a.equals(b);
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof FacetFilter)) {
+            return false;
+        }
+        FacetFilter o = (FacetFilter)other;
+        return equals(o.name, name) && equals(o.ids, ids);
+    }
+
+    public void collectIds(StringBuilder sb) {
+        if (ids != null) {
+            if (sb.length() > 0) {
+                sb.append(' ');
+            }
+            sb.append(ids);
+        }
+    }
+
+    public Recommendation.Facet toFacet() {
+        return new Recommendation.Facet(name, ids);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 2012 +0200
@@ -0,0 +1,50 @@
+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 Theme theme;
+
+
+    public FacetRecord(Theme theme) {
+        this.theme = theme;
+
+        setActive(theme.getActive() == 1);
+        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) {
+        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/MapConfig.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,62 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class MapConfig implements Serializable {
+
+    public static final String ATTR_SRID           = "srid";
+    public static final String ATTR_MAX_EXTENT     = "max_extent";
+    public static final String ATTR_INITIAL_EXTENT = "initial_extent";
+
+
+    protected Map<String, String> attributes;
+
+
+    public MapConfig() {
+        attributes = new HashMap<String, String>();
+    }
+
+
+    public void setAttribute(String key, String value) {
+        attributes.put(key, value);
+    }
+
+
+    public String getAttribute(String key) {
+        return attributes.get(key);
+    }
+
+
+    public void setSrid(String srid) {
+        setAttribute(ATTR_SRID, srid);
+    }
+
+
+    public String getSrid() {
+        return getAttribute(ATTR_SRID);
+    }
+
+
+    public void setMaxExtent(String maxExtent) {
+        setAttribute(ATTR_MAX_EXTENT, maxExtent);
+    }
+
+
+    public String getMaxExtent() {
+        return getAttribute(ATTR_MAX_EXTENT);
+    }
+
+
+    public void setInitialExtent(String initialExtent) {
+        setAttribute(ATTR_INITIAL_EXTENT, initialExtent);
+    }
+
+
+    public String getInitialExtent() {
+        return getAttribute(ATTR_INITIAL_EXTENT);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/MapInfo.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,71 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+public class MapInfo implements Serializable {
+
+    protected String river;
+    protected int    srid;
+    protected BBox   bbox;
+    protected String wmsUrl;
+    protected String backgroundWmsUrl;
+    protected String backgroundWmsLayers;
+
+
+    public MapInfo() {
+    }
+
+
+    public MapInfo(
+        String river,
+        int    srid,
+        BBox   bbox,
+        String wmsUrl,
+        String backgroundWmsUrl,
+        String backgroundWmsLayers)
+    {
+        this.river               = river;
+        this.srid                = srid;
+        this.bbox                = bbox;
+        this.wmsUrl              = wmsUrl;
+        this.backgroundWmsUrl    = backgroundWmsUrl;
+        this.backgroundWmsLayers = backgroundWmsLayers;
+    }
+
+
+    public String getRiver() {
+        return river;
+    }
+
+
+    public int getSrid() {
+        return srid;
+    }
+
+
+    public String getProjection() {
+        return "EPSG:" + srid;
+    }
+
+
+    public BBox getBBox() {
+        return bbox;
+    }
+
+
+    public String getWmsUrl() {
+        return wmsUrl;
+    }
+
+
+    public String getBackgroundWmsUrl() {
+        return backgroundWmsUrl;
+    }
+
+
+    public String getBackgroundWmsLayers() {
+        return backgroundWmsLayers;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/MapMode.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,39 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.OutputTab;
+import de.intevation.flys.client.client.ui.map.MapOutputTab;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapMode extends DefaultOutputMode {
+
+    public MapMode() {
+    }
+
+
+    public MapMode(String name, String desc, String mimeType) {
+        super(name, desc, mimeType);
+    }
+
+
+    public MapMode(
+        String name,
+        String descrition,
+        String mimeType,
+        List<Facet> facets)
+    {
+        super(name, descrition, mimeType, facets);
+    }
+
+
+    @Override
+    public OutputTab createOutputTab(String t, Collection c, CollectionView p) {
+        return new MapOutputTab(t, c, this, p);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/OutFilter.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,62 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OutFilter
+implements   Serializable
+{
+    protected String out;
+
+    protected List<FacetFilter> facetFilters;
+
+    public OutFilter() {
+        facetFilters = new ArrayList<FacetFilter>();
+    }
+
+    public OutFilter(String out) {
+        this();
+        this.out = out;
+    }
+
+    public String getOut() {
+        return out;
+    }
+
+    public void setOut(String out) {
+        this.out = out;
+    }
+
+    public void add(String name, String num) {
+        FacetFilter facetFilter = new FacetFilter(name, num);
+        if (!facetFilters.contains(facetFilter)) {
+            facetFilters.add(facetFilter);
+        }
+    }
+
+    public List<FacetFilter> getFacetFilters() {
+        return facetFilters;
+    }
+
+    public void setFacetFilters(List<FacetFilter> facetFilters) {
+        this.facetFilters = facetFilters;
+    }
+
+    public void collectIds(StringBuilder sb) {
+        for (FacetFilter facetFilter: facetFilters) {
+            facetFilter.collectIds(sb);
+        }
+    }
+
+    public List<Recommendation.Facet> toFacets() {
+        List<Recommendation.Facet> facets =
+            new ArrayList<Recommendation.Facet>(facetFilters.size());
+        for (FacetFilter facetFilter: facetFilters) {
+            facets.add(facetFilter.toFacet());
+        }
+        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/OutputMode.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,105 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.OutputTab;
+
+
+/**
+ * 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();
+
+
+    /**
+     * Returns the type of this mode.
+     *
+     * @return the type of this mode.
+     */
+    String getType();
+
+
+    /**
+     * 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();
+
+
+    /**
+     * Returns an OutputTab that is used to render the output mode.
+     *
+     * @param t The title.
+     * @param c The Collection.
+     * @param p The parent CollectionView.
+     *
+     * @return an OutputTab.
+     */
+    OutputTab createOutputTab(String t, Collection c, CollectionView p);
+}
+// vim:set ts=4 sw=4 si et sta 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/Recommendation.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,211 @@
+package de.intevation.flys.client.shared.model;
+
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Recommendation implements Serializable {
+
+    /** Index and name of a facet. */
+    public static class Facet implements Serializable {
+
+        protected String name;
+        protected String index;
+
+        public Facet() {
+        }
+
+        public Facet(String name, String index) {
+            this.name  = name;
+            this.index = index;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getIndex() {
+            return index;
+        }
+
+
+        @Override
+        public int hashCode() {
+            int hash = 0;
+            if (getName() != null) {
+                hash += getName().hashCode();
+            }
+            if (getIndex() != null) {
+                hash += getIndex().hashCode();
+            }
+            return hash;
+        }
+
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof Facet) || other == null) {
+                return false;
+            }
+            Facet facet = (Facet) other;
+            return (same(facet.getIndex(), this.getIndex()))
+                && (same(facet.getName(),  this.getName()));
+        }
+    } // class Facet
+
+
+    /** Mapping of outnames to Facet-Lists. */
+    public static class Filter implements Serializable {
+
+        protected Map<String, List<Facet>> outs;
+
+        public Filter() {
+            outs = new HashMap<String, List<Facet>>();
+        }
+
+        public void add(String out, List<Facet> facets) {
+            outs.put(out, facets);
+        }
+
+        public Map<String, List<Facet>> getOuts() {
+            return outs;
+        }
+
+
+        @Override
+        public int hashCode() {
+            if (getOuts() != null) {
+                return getOuts().hashCode();
+            }
+            return 0;
+        }
+
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof Filter) || other == null) {
+                return false;
+            }
+            Filter filter = (Filter) other;
+            return Recommendation.same(filter.getOuts(), this.getOuts());
+        }
+    } // class Filter
+
+    protected String factory;
+    protected String ids;
+    protected String masterArtifact;
+    protected Filter filter;
+    protected String displayName = null;
+
+    public Recommendation() {
+    }
+
+    public Recommendation(String factory, String ids) {
+        this(factory, ids, null, null);
+    }
+
+    public Recommendation(
+        String factory,
+        String ids,
+        String masterArtifact,
+        Filter filter
+    ) {
+        this.factory        = factory;
+        this.ids            = ids;
+        this.masterArtifact = masterArtifact;
+        this.filter         = filter;
+    }
+
+    public String getFactory() {
+        return factory;
+    }
+
+    public void setFactory(String factory) {
+        this.factory = factory;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getDisplayName() {
+        return this.displayName;
+    }
+
+    public String getIDs() {
+        return ids;
+    }
+
+    public String getMasterArtifact() {
+        return masterArtifact;
+    }
+
+    public void setMasterArtifact(String masterArtifact) {
+        this.masterArtifact = masterArtifact;
+    }
+
+    public Filter getFilter() {
+        return filter;
+    }
+
+    public void setFilter(Filter filter) {
+        this.filter = filter;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (getFactory() != null)
+            ? getFactory().hashCode()
+            : 0;
+        hash += (getIDs() != null)
+            ? getIDs().hashCode()
+            : 0;
+        hash += (getFilter() != null)
+            ? getFilter().hashCode()
+            : 0;
+        hash += (getMasterArtifact() != null)
+            ? getMasterArtifact().hashCode()
+            : 0;
+        return hash;
+    }
+
+
+    /**
+     * Null-pointer guarded equals.
+     * Two null's are assumed equal (returns true);
+     * @param a Object to compare against parameter b.
+     * @param b Object to compare against parameter a.
+     * @return true if either a and b are null or a.equals(b) returns true.
+     */
+    protected static boolean same(Object a, Object b) {
+        // Do null-check.
+        if (a == null) {
+            return b == null;
+        } else if (b == null) {
+            return false;
+        }
+        return a.equals(b);
+    }
+
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof Recommendation) || other == null) {
+            return false;
+        }
+        Recommendation rec = (Recommendation) other;
+        return (same(this.getFactory(), rec.getFactory()))
+            && (same(this.getIDs(),     rec.getIDs()))
+            && (same(this.getFilter(),  rec.getFilter()))
+            && (same(this.getMasterArtifact(), rec.getMasterArtifact()));
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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:51 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/Style.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,100 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class Style implements Serializable {
+
+    /** The theme name. */
+    protected String name;
+
+    /** The facet. */
+    protected String facet;
+
+    /** The theme index. */
+    protected int index;
+
+    /** List of theme attribute settings. */
+    protected List<StyleSetting> settings;
+
+
+    /**
+     * Create a new style for theme attribution.
+     */
+    public Style() {
+        settings = new ArrayList<StyleSetting>();
+    }
+
+
+    /**
+     * Append a new style setting.
+     * @param setting A theme attribution setting.
+     */
+    public void appendStyleSetting(StyleSetting setting) {
+        settings.add(setting);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setFacet(String facet) {
+        this.facet = facet;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getFacet() {
+        return this.facet;
+    }
+
+    public int getIndex() {
+        return this.index;
+    }
+
+
+    /**
+     * Getter for a theme attribution setting.
+     * @return The style setting.
+     */
+    public StyleSetting getSetting(String name) {
+        for (int i = 0; i < settings.size (); i++) {
+            if (settings.get(i).getName().equals(name)) {
+                return settings.get(i);
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Getter for number of settings.
+     * @return The size of the settings list.
+     */
+    public int getNumSettings () {
+        return settings.size();
+    }
+
+
+    /**
+     * Getter for style setting.
+     * @param i The index in the settings list.
+     *
+     * @return The style setting at the given index.
+     */
+    public StyleSetting getSetting(int i) {
+        return this.settings.get(i);
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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/StyleSetting.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,97 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class StyleSetting implements Serializable {
+
+    /**The settings name.*/
+    protected String name;
+
+    /** The default value.*/
+    protected String defaultValue;
+
+    /**The display name*/
+    protected String displayName;
+
+    /**Hints.*/
+    protected String hints;
+
+    /**The type*/
+    protected String type;
+
+
+    /**
+     * Create a new StyleSetting for theme attribution.
+     */
+    public StyleSetting() {
+    }
+
+
+    /**
+     * Create a new StyleSetting for theme attribution.
+     * @param name The attribute name.
+     * @param defaultValue The current value.
+     * @param displayName The name to show in a dialog.
+     * @param hints Hints.
+     * @param type The attribute type.
+     */
+    public StyleSetting(
+        String name,
+        String defaultValue,
+        String displayName,
+        String hints,
+        String type)
+    {
+        this.name = name;
+        this.defaultValue = defaultValue;
+        this.displayName = displayName;
+        this.hints = hints;
+        this.type = type;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setDefaultValue(String value) {
+        this.defaultValue = value;
+    }
+
+    public void setDisplayName(String name) {
+        this.displayName = name;
+    }
+
+    public void setHints(String hints) {
+        this.hints = hints;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getDefaultValue() {
+        return this.defaultValue;
+    }
+
+    public String getDisplayName() {
+        return this.displayName;
+    }
+
+    public String getHints() {
+        return this.hints;
+    }
+
+    public String getType() {
+        return this.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/shared/model/Theme.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,35 @@
+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();
+
+    int getActive();
+
+    void setActive(int active);
+
+    int getVisible();
+
+    void setVisible(int visible);
+
+    String getArtifact();
+
+    String getFacet();
+
+    String getDescription();
+
+    void setDescription(String description);
+
+    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:51 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/ToLoad.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,119 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.Serializable;
+
+public class ToLoad implements Serializable
+{
+
+    public class StringPair {
+        public String first;
+        public String second;
+        public StringPair(String first, String second) {
+            this.first = first;
+            this.second = second;
+        }
+        public int hashCode() {
+            return first.hashCode() + second.hashCode();
+        }
+        public boolean equals(StringPair other) {
+            return (this.second.equals(other.second)) && (this.first.equals(other.first));
+        }
+    }
+    public static final String SYNTHETIC_KEY = "key-";
+
+    protected Map<String, Map<StringPair, ArtifactFilter>> artifacts;
+
+    public ToLoad() {
+        artifacts = new HashMap<String, Map<StringPair, ArtifactFilter>>();
+    }
+
+    public static final String uniqueKey(Map<?, ?> map) {
+        int idx = map.size();
+
+        String key = SYNTHETIC_KEY + idx;
+        while (map.containsKey(key)) {
+            key = SYNTHETIC_KEY + ++idx;
+        }
+        return key;
+    }
+
+    public void add(
+        String artifactName,
+        String factory,
+        String out,
+        String name,
+        String ids,
+        String displayName
+    ) {
+        if (artifactName == null) {
+            artifactName = uniqueKey(artifacts);
+        }
+
+        Map<StringPair, ArtifactFilter> artifact = artifacts.get(artifactName);
+
+        if (artifact == null) {
+            artifact = new HashMap<StringPair, ArtifactFilter>();
+            artifacts.put(artifactName, artifact);
+        }
+
+        ArtifactFilter filter = artifact.get(factory);
+        if (filter == null) {
+            filter = new ArtifactFilter(factory);
+            artifact.put(new StringPair(factory, displayName), filter);
+        }
+
+        filter.add(out, name, ids);
+    }
+
+    public boolean isEmpty() {
+        return artifacts.isEmpty();
+    }
+
+    public List<Recommendation> toRecommendations() {
+        List<Recommendation> recommendations = new ArrayList<Recommendation>();
+
+        for (Map.Entry<String, Map<StringPair, ArtifactFilter>> all:
+            artifacts.entrySet()
+        ) {
+            String masterArtifact = all.getKey();
+
+            if (masterArtifact.startsWith(SYNTHETIC_KEY)) { // system data
+                masterArtifact = null;
+            }
+
+            for (Map.Entry<StringPair, ArtifactFilter> entry:
+                all.getValue().entrySet()
+            ) {
+                StringPair pair         = entry.getKey();
+                String factory = pair.first;
+                ArtifactFilter artifactFilter = entry.getValue();
+
+                String                ids;
+                Recommendation.Filter filter;
+
+                if (masterArtifact == null) { // system data
+                    ids    = artifactFilter.collectIds();
+                    filter = null;
+                }
+                else { // user specific
+                    ids    = null;
+                    filter = artifactFilter.toFilter();
+                }
+
+                Recommendation recommendation = new Recommendation(
+                    factory, ids, masterArtifact, filter);
+                recommendation.setDisplayName(pair.second);
+
+                recommendations.add(recommendation);
+            }
+        }
+
+        return recommendations;
+    }
+}
+// vim:set ts=4 sw=4 si et sta 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:51 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:51 2012 +0200
@@ -0,0 +1,40 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+
+/**
+ * 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 WINFOArtifact(
+        String                   uuid,
+        String                   hash,
+        boolean                  inBackground,
+        List<CalculationMessage> messages
+    ) {
+        super(uuid, hash, inBackground, messages);
+    }
+
+
+    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:51 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:51 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:51 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:51 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/java/de/intevation/flys/client/shared/model/ZoomObj.java	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+public class ZoomObj implements Serializable {
+
+    protected double a;
+    protected double b;
+    protected double c;
+    protected double d;
+
+
+    public ZoomObj() {
+    }
+
+
+    public ZoomObj(double a, double b, double c, double d) {
+        this.a = a;
+        this.b = b;
+        this.c = c;
+        this.d = d;
+    }
+
+
+    public double[] getZoom() {
+        return new double[] { a, b, c, d };
+    }
+}
+// vim:set ts=4 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:51 2012 +0200
@@ -0,0 +1,130 @@
+/** 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;
+}
+
+.oldParameters {
+    border-bottom: 1px solid black;
+    padding: 5px;
+}
+
+label {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 11px;
+  font-style: normal;
+  font-weight: 400;
+  color: #000000;
+}
+
+.loading-box {
+    border: 1px solid black;
+}
+
+.loading-title {
+    background: white;
+    padding-left: 10px;
+    color: black;
+    font-size: 15px;
+    font-style: normal;
+    padding: 10px;
+}
+
+.loading-message {
+    background: white;
+    color: black;
+    font-size: 11px;
+    font-style: normal;
+    padding-left: 10px;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/webapp/FLYS.html	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,55 @@
+<!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>
+
+    <!-- TODO We should NOT include OpenLayers from remote. -->
+    <script type="text/javascript" language="javascript" src="OpenLayers-2.11/OpenLayers.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/log4j.properties	Fri Sep 28 12:14:51 2012 +0200
@@ -0,0 +1,18 @@
+log4j.rootLogger=DEBUG, FLYS
+
+########## INTERNAL PACKAGES
+log4j.category.de.intevation.flys.client.server=DEBUG
+
+
+########## EXTERNAL PACKAGES
+log4j.category.org.apache.http=ERROR
+log4j.category.de.intevation.artifacts.httpclient=WARN
+
+########## APPENDER SETTINGS
+log4j.appender.FLYS.layout=org.apache.log4j.PatternLayout
+log4j.appender.FLYS.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
+
+log4j.appender.FLYS=org.apache.log4j.RollingFileAppender
+log4j.appender.FLYS.File=/tmp/flys-client.log
+log4j.appender.FLYS.MaxFileSize=5000KB
+log4j.appender.FLYS.MaxBackupIndex=3
--- /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:51 2012 +0200
@@ -0,0 +1,332 @@
+<?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>
+
+    <context-param>
+        <param-name>server-url</param-name>
+        <param-value>http://localhost:8181</param-value>
+    </context-param>
+  
+  <!-- Servlets -->
+  <servlet>
+    <servlet-name>BaseServlet</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.BaseServlet</servlet-class>
+
+    <init-param>
+        <param-name>log4j-properties</param-name>
+        <param-value>/WEB-INF/log4j.properties</param-value>
+    </init-param>
+
+    <load-on-startup>1</load-on-startup>
+  </servlet>
+
+  <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>feed</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.FeedServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>feed</servlet-name>
+    <url-pattern>/flys/feed</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>load-artifact</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.LoadArtifactServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>load-artifact</servlet-name>
+    <url-pattern>/flys/load-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>
+    <servlet-name>MapOutputService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.MapOutputServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>MapOutputService</servlet-name>
+    <url-pattern>/flys/map</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>ChartOutputService</servlet-name>
+    <url-pattern>/flys/chart</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>DistanceInfoXML</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.DistanceInfoXML</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>DistanceInfoXML</servlet-name>
+    <url-pattern>/flys/distanceinfoxml</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>CollectionItemAttributeService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.CollectionItemAttributeServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>CollectionItemAttributeService</servlet-name>
+    <url-pattern>/flys/collection-item-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>
+
+  <servlet>
+    <servlet-name>MapInfoService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.MapInfoServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>MapInfoService</servlet-name>
+    <url-pattern>/flys/map-info</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>DescribeArtifactService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.DescribeArtifactServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>DescribeArtifactService</servlet-name>
+    <url-pattern>/flys/describe</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>remove-artifact</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.RemoveArtifactServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>remove-artifact</servlet-name>
+    <url-pattern>/flys/remove-artifact</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/adjustElevation.png has changed
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/dam.png has changed
Binary file flys-client/src/main/webapp/images/digitize.png has changed
Binary file flys-client/src/main/webapp/images/digitize_Selected.png has changed
Binary file flys-client/src/main/webapp/images/ditch.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-1px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-2px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-3px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-4px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-5px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-6px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-7px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-8px.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-dash-10-5.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-dash-10.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-dash-20-10.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-dash-20-5-15-5.png has changed
Binary file flys-client/src/main/webapp/images/linestyle-dash-30-10.png has changed
Binary file flys-client/src/main/webapp/images/measureLine.png has changed
Binary file flys-client/src/main/webapp/images/measureLine_Selected.png has changed
Binary file flys-client/src/main/webapp/images/measurePolygon.png has changed
Binary file flys-client/src/main/webapp/images/measurePolygon_Selected.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/pipe1.png has changed
Binary file flys-client/src/main/webapp/images/pipe2.png has changed
Binary file flys-client/src/main/webapp/images/removeFeature.png has changed
Binary file flys-client/src/main/webapp/images/ring_dike.png has changed
Binary file flys-client/src/main/webapp/images/save.gif has changed
Binary file flys-client/src/main/webapp/images/selectFeature.png has changed
Binary file flys-client/src/main/webapp/images/selectFeature_Selected.png 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-back.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:51 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