changeset 9288:82c67b859aa7

bundu.bezugswst worklflow incl. service impl for mainValues to be calculated
author gernotbelger
date Tue, 24 Jul 2018 10:39:03 +0200
parents 6c88ad449c83
children fe207a8699f7 3729434f6dec
files artifacts/doc/conf/artifacts/bundu.xml artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/BunduWstWQFixing.java artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/FixationChoice.java artifacts/src/main/java/org/dive4elements/river/artifacts/services/AbstractMainValuesService.java artifacts/src/main/java/org/dive4elements/river/artifacts/services/DynamicMainValuesService.java artifacts/src/main/java/org/dive4elements/river/artifacts/services/MainValuesService.java gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties gwt-client/src/main/java/org/dive4elements/river/client/client/services/DynamicMainValuesService.java gwt-client/src/main/java/org/dive4elements/river/client/client/services/DynamicMainValuesServiceAsync.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractUIProvider.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractWQAdaptedInputPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WQAdaptedInputPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/bundu/BunduWstWQPanel.java gwt-client/src/main/java/org/dive4elements/river/client/server/AbstractMainValuesServiceImpl.java gwt-client/src/main/java/org/dive4elements/river/client/server/DynamicMainValuesServiceImpl.java gwt-client/src/main/java/org/dive4elements/river/client/server/WQInfoServiceImpl.java
diffstat 18 files changed, 1734 insertions(+), 1228 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/bundu.xml	Tue Jul 24 10:36:18 2018 +0200
+++ b/artifacts/doc/conf/artifacts/bundu.xml	Tue Jul 24 10:39:03 2018 +0200
@@ -327,7 +327,7 @@
 
     <state id="state.bundu.yearselect" helpText="state.bundu.yearselect" description="state.bundu.yearselect" state="org.dive4elements.river.artifacts.bundu.bezugswst.FixationPeriod">
       <data name="singleyear" type="String" />
-      <data name="events_temp" type="intarray" /> <!--necessary, because they will be set automatically -->
+      <data name="events_temp" type="String" /> <!-- String. -->
     </state>
 
 
@@ -390,8 +390,9 @@
     </transition>
 
     <state id="state.bundu.wst.qs" description="state.bundu.wst.qs" helpText="help.state.bundu.wst.qs" state="org.dive4elements.river.artifacts.bundu.bezugswst.BunduWstWQFixing">
-      <data name="wq_isq" type="String" />
+      <data name="bundu.wst.mode" type="String" /> 
       <data name="wq_values" type="WQTriple" />
+      <data name="bundu.wst.ud_value" type ="Integer"/>
     </state>
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/BunduWstWQFixing.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/BunduWstWQFixing.java	Tue Jul 24 10:39:03 2018 +0200
@@ -12,7 +12,6 @@
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.common.utils.XMLUtils;
 import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.model.RangeWithValues;
 import org.dive4elements.river.artifacts.states.WQAdapted;
 import org.w3c.dom.Element;
 
@@ -23,6 +22,8 @@
  */
 public class BunduWstWQFixing extends WQAdapted {
 
+    private static final long serialVersionUID = 1L;
+
     /** The log used in this state. */
     private static Logger log = Logger.getLogger(BunduWstWQFixing.class);
 
@@ -31,45 +32,30 @@
         return "bundu_wst_wq_panel";
     }
 
-    /** Simple sanity check if values are positive numbers **/
     @Override
     public boolean validate(final Artifact artifact) throws IllegalArgumentException {
-        log.debug("BunduWstWQFixing.validate");
-
-        final RangeWithValues[] rwvs = extractInput(getData((D4EArtifact) artifact, "wq_values"));
-
-        if (rwvs == null) {
-            throw new IllegalArgumentException("error_missing_wq_data");
-        }
 
-        for (final RangeWithValues rwv : rwvs) {
-            final double[] values = rwv.getValues();
-            for (final double val : values) {
-                if (val <= 0) {
-                    throw new IllegalArgumentException("error_validate_positive");
-                }
-            }
-        }
-
+        // Validation is done on client side. No wrong inputs should arrive here.
+        // Validation in Super is not suitable for this case
         return true;
     }
 
     // adding bezugsjahr and dauer to client
-
     @Override
     protected Element[] createItems(final XMLUtils.ElementCreator ec, final Artifact artifact, final String name, final CallContext context) {
 
         final Element[] defaultElements = super.createItems(ec, artifact, name, context);
-
-        final BunduAccess access = new BunduAccess((D4EArtifact) artifact);
+        if (name.equals("bundu.wst.mode")) { // random-name eines DataItems vom state (s. bundu.xml), sodass verhindert wird, dass an alle DataItems
+                                             // length+bezugsjahr drangehängt werden
+            final BunduAccess access = new BunduAccess((D4EArtifact) artifact);
 
-        final int bezugsjahr = access.getBezugsJahr();
-        final int qSeriesLength = access.getQSeriesLength();
-        final String[] keys = new String[] { "ignore_q_series_length", "ignore_bezugsjahr" };
-        final String[] values = new String[] { String.valueOf(qSeriesLength), String.valueOf(bezugsjahr) };
+            final int bezugsjahr = access.getBezugsJahr();
+            final int qSeriesLength = access.getQSeriesLength();
+            final String[] keys = new String[] { "ignore_q_series_length", "ignore_bezugsjahr" };
+            final String[] values = new String[] { String.valueOf(qSeriesLength), String.valueOf(bezugsjahr) };
 
-        return FixationChoice.appendElements(ec, defaultElements, keys, values);
-
+            return FixationChoice.appendElements(ec, defaultElements, keys, values);
+        } else
+            return defaultElements;
     }
-
-}
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/FixationChoice.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/FixationChoice.java	Tue Jul 24 10:39:03 2018 +0200
@@ -65,14 +65,15 @@
     public static final Element[] appendElements(final ElementCreator ec, final Element[] defaultElements, final String[] keys, final String[] values) {
         assert (values.length == keys.length);
         // make bigger array
-        final Element[] defaultAndAddedElements = new Element[defaultElements.length + values.length];
+        final int defaultLength = defaultElements == null ? 0 : defaultElements.length;
+        final Element[] defaultAndAddedElements = new Element[defaultLength + values.length];
 
-        for (int i = 0; i < defaultElements.length; i++) {
+        for (int i = 0; i < defaultLength; i++) {
             defaultAndAddedElements[i] = defaultElements[i];
         }
         // add the new values:
         for (int j = 0; j < keys.length; j++) {
-            defaultAndAddedElements[defaultElements.length + j] = createItem(ec, new String[] { keys[j], values[j] });
+            defaultAndAddedElements[defaultLength + j] = createItem(ec, new String[] { keys[j], values[j] });
         }
         return defaultAndAddedElements;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/AbstractMainValuesService.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,255 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.services;
+
+import static org.dive4elements.river.backend.utils.EpsilonComparator.CMP;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.MainValueType;
+import org.dive4elements.river.model.NamedMainValue;
+import org.dive4elements.river.model.OfficialLine;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.River;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+abstract class AbstractMainValuesService extends D4EService {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final class MainValuesServiceException extends Exception {
+
+        private static final long serialVersionUID = 1L;
+
+        public MainValuesServiceException(final String message) {
+            super(message);
+        }
+    }
+
+    /** The log that is used by this service. */
+    private static Logger log = Logger.getLogger(AbstractMainValuesService.class);
+
+    /** XPath that points to the river definition of the incoming request. */
+    private static final String XPATH_RIVER = "/art:mainvalues/art:river/text()";
+
+    /** XPath that points to the start definition of the incoming request. */
+    private static final String XPATH_START = "/art:mainvalues/art:start/text()";
+
+    /** The XPath that points to the end definition of the incoming request. */
+    private static final String XPATH_END = "/art:mainvalues/art:end/text()";
+
+    protected static final Document error(final String msg) {
+        log.debug(msg);
+        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 final River getRequestedRiver(final Document data) throws MainValuesServiceException {
+        log.debug("MainValuesService.getRequestedRiver");
+
+        String riverStr = XMLUtils.xpathString(data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+
+        if (riverStr != null && (riverStr = riverStr.trim()).length() > 0)
+            return RiverFactory.getRiver(riverStr);
+
+        throw new MainValuesServiceException("no river found.");
+    }
+
+    protected final Gauge getRequestedGauge(final Document data, final River river) throws MainValuesServiceException {
+
+        final double[] minmax = getRequestedStartEnd(data, river);
+        final Gauge gauge = river.determineRefGauge(minmax, CMP.compare(minmax[0], minmax[1]) != 0);
+        if (gauge == null)
+            throw new MainValuesServiceException("no gauge found.");
+
+        return gauge;
+    }
+
+    /**
+     * 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.
+     */
+    private double[] getRequestedStartEnd(final Document data, final River river) {
+        log.debug("MainValuesService.getStartEnd");
+
+        final String startStr = XMLUtils.xpathString(data, XPATH_START, ArtifactNamespaceContext.INSTANCE);
+
+        final String endStr = XMLUtils.xpathString(data, XPATH_END, ArtifactNamespaceContext.INSTANCE);
+
+        if (startStr == null || endStr == null) {
+            return river.determineMinMaxDistance();
+        }
+
+        try {
+            final double start = Double.parseDouble(startStr);
+            final double end = Double.parseDouble(endStr);
+
+            if (log.isDebugEnabled()) {
+                log.debug("Found start: " + start);
+                log.debug("Found end: " + end);
+            }
+
+            return new double[] { start, end };
+        }
+        catch (final NumberFormatException nfe) {
+            log.warn(nfe, nfe);
+            return river.determineMinMaxDistance();
+        }
+    }
+
+    protected final Document buildDocument(final River river, final Gauge gauge, final List<MainValue> mainValues, final Object context) {
+        log.debug("MainValuesService.buildDocument");
+
+        final Document doc = XMLUtils.newDocument();
+
+        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        final Element rootEl = cr.create("service");
+        cr.addAttr(rootEl, "name", "mainvalues");
+
+        doc.appendChild(rootEl);
+
+        appendMetaInformation(doc, rootEl, river, gauge, context);
+        appendMainValues(doc, rootEl, mainValues, river.getId(), 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.
+     */
+    private void appendMetaInformation(final Document doc, final Element root, final River river, final Gauge gauge, final Object context) {
+        log.debug("MainValuesService.appendMetaInformation");
+
+        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        final Range range = gauge.getRange();
+
+        final Element riverEl = cr.create("river");
+        cr.addAttr(riverEl, "name", river.getName());
+
+        final 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);
+    }
+
+    /** Checks i a main value has an official associated, */
+    private static boolean hasOfficialLine(final NamedMainValue nmv, final Integer riverId) {
+        for (final OfficialLine ol : nmv.getOfficialLines()) {
+            if (ol.getWstColumn().getWst().getRiver().getId().equals(riverId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Append xml representation of main values to document. */
+    private void appendMainValues(final Document doc, final Element root, final List<MainValue> mainValues, final Integer riverId, final Object context) {
+        log.debug("MainValuesService.appendMainValues");
+
+        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        final Element list = cr.create("mainvalues");
+
+        for (final MainValue mainValue : mainValues) {
+            final Element newEl = buildMainValueElement(doc, mainValue, riverId, 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.
+     */
+    private Element buildMainValueElement(final Document doc, final MainValue mainValue, final Integer riverId, final Object context) {
+        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        final NamedMainValue namedMainValue = mainValue.getMainValue();
+        final MainValueType mainValueType = namedMainValue.getType();
+
+        final Element el = cr.create("mainvalue");
+
+        cr.addAttr(el, "value", mainValue.getValue().toString());
+        cr.addAttr(el, "name", namedMainValue.getName());
+        cr.addAttr(el, "type", mainValueType.getName());
+        if (mainValue.getTimeInterval() != null) {
+            if (mainValue.getTimeInterval().getStartTime() != null) {
+                cr.addAttr(el, "starttime", Long.toString(mainValue.getTimeInterval().getStartTime().getTime()));
+            }
+            if (mainValue.getTimeInterval().getStopTime() != null) {
+                cr.addAttr(el, "stoptime", Long.toString(mainValue.getTimeInterval().getStopTime().getTime()));
+            }
+        }
+
+        if (hasOfficialLine(namedMainValue, riverId)) {
+            cr.addAttr(el, "official", "true");
+        }
+
+        return el;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/DynamicMainValuesService.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,146 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.services;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.MainValueType;
+import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
+import org.dive4elements.river.model.NamedMainValue;
+import org.dive4elements.river.model.OfficialLine;
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.TimeInterval;
+import org.w3c.dom.Document;
+
+/**
+ * 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 DynamicMainValuesService extends AbstractMainValuesService {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String XPATH_START_YEAR = "/art:mainvalues/art:startYear/text()";
+
+    private static final String XPATH_END_YEAR = "/art:mainvalues/art:endYear/text()";
+
+    @Override
+    public Document doProcess(final Document data, final GlobalContext context, final CallMeta callMeta) {
+        try {
+
+            final River river = getRequestedRiver(data);
+            final Gauge gauge = getRequestedGauge(data, river);
+            final Date startTime = getRequestedStartYear(data);
+            final Date endTime = getRequestedEndYear(data);
+
+            final List<MainValue> mainValues = getMainValues(river, gauge, startTime, endTime);
+
+            return buildDocument(river, gauge, mainValues, context);
+        }
+        catch (final MainValuesServiceException e) {
+            e.printStackTrace();
+            return error(e.getMessage());
+        }
+    }
+
+    private Date getRequestedStartYear(final Document data) throws MainValuesServiceException {
+
+        final String startStr = XMLUtils.xpathString(data, XPATH_START_YEAR, ArtifactNamespaceContext.INSTANCE);
+
+        if (startStr == null)
+            throw new MainValuesServiceException("no start year");
+
+        try {
+            final int year = Integer.parseInt(startStr);
+
+            // FIXME: timezone? probably must match timezone of database
+            final Calendar cal = Calendar.getInstance();
+            cal.clear();
+            cal.set(year, 0, 1);
+            return cal.getTime();
+        }
+        catch (final NumberFormatException e) {
+            e.printStackTrace();
+            throw new MainValuesServiceException("invalid start year");
+        }
+    }
+
+    private Date getRequestedEndYear(final Document data) throws MainValuesServiceException {
+
+        final String endStr = XMLUtils.xpathString(data, XPATH_END_YEAR, ArtifactNamespaceContext.INSTANCE);
+
+        if (endStr == null)
+            throw new MainValuesServiceException("no end year");
+
+        try {
+            final int year = Integer.parseInt(endStr);
+
+            // FIXME: timezone? probably must match timezone of database
+            final Calendar cal = Calendar.getInstance();
+            cal.clear();
+            cal.set(year, 11, 31);
+            return cal.getTime();
+        }
+        catch (final NumberFormatException e) {
+            e.printStackTrace();
+            throw new MainValuesServiceException("invalid end year");
+        }
+    }
+
+    /**
+     * 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.
+     * @param endYear
+     * @param startYear
+     *
+     * @return a document that includes the main values of the specified river
+     *         at the specified gauge.
+     */
+    protected List<MainValue> getMainValues(final River river, final Gauge gauge, final Date startTime, final Date endTime) {
+
+        // TODO: compute our own main values from the discharge timeseries.
+
+        // final List<MainValue> mainValues = gauge.getMainValues();
+        final List<MainValue> mainValues = new ArrayList<>();
+
+        final MainValue myMain = new MainValue();
+
+        // TODO: fetch real NamedMainValue from database: GLQ20, MNQ, MQ, MHQ, HQ5 + Dauerzahlen
+        final NamedMainValue mainValue = new NamedMainValue("Testname", new MainValueType(MainValueTypeKey.Q.getName()));
+        mainValue.setOfficialLines(new ArrayList<OfficialLine>());
+
+        myMain.setMainValue(mainValue);
+        // FIXME: compute value
+        myMain.setValue(new BigDecimal("1234.567"));
+
+        final TimeInterval timeInterval = new TimeInterval(startTime, endTime);
+        myMain.setTimeInterval(timeInterval);
+
+        mainValues.add(myMain);
+
+        return mainValues;
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/MainValuesService.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/MainValuesService.java	Tue Jul 24 10:39:03 2018 +0200
@@ -11,29 +11,12 @@
 import java.util.List;
 
 import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
 import org.dive4elements.artifacts.CallMeta;
 import org.dive4elements.artifacts.GlobalContext;
-
-import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
-import org.dive4elements.artifacts.common.utils.XMLUtils;
-import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
-
 import org.dive4elements.river.model.Gauge;
 import org.dive4elements.river.model.MainValue;
-import org.dive4elements.river.model.MainValueType;
-import org.dive4elements.river.model.NamedMainValue;
-import org.dive4elements.river.model.OfficialLine;
-import org.dive4elements.river.model.Range;
 import org.dive4elements.river.model.River;
-
-import org.dive4elements.river.artifacts.model.RiverFactory;
-
-import static org.dive4elements.river.backend.utils.EpsilonComparator.CMP;
-
+import org.w3c.dom.Document;
 
 /**
  * This service returns the main values of a river's gauge based on the start
@@ -41,143 +24,42 @@
  *
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
-public class MainValuesService extends D4EService {
-
-    /** The log that is used by this service.*/
-    private static Logger log = Logger.getLogger(MainValuesService.class);
-
-    /** XPath that points to the river definition of the incoming request.*/
-    public static final String XPATH_RIVER =
-        "/art:mainvalues/art:river/text()";
-
-    /** XPath that points to the start definition of the incoming request.*/
-    public static final String XPATH_START =
-        "/art:mainvalues/art:start/text()";
+public class MainValuesService extends AbstractMainValuesService {
 
-    /** The XPath that points to the end definition of the incoming request.*/
-    public static final String XPATH_END = "/art:mainvalues/art:end/text()";
-
-    protected CallMeta callMeta;
-
+    private static final long serialVersionUID = 1L;
 
-    /**
-     * The default constructor.
-     */
-    public MainValuesService() {
-    }
-
-    private static final Document error(String msg) {
-        log.debug(msg);
-        return XMLUtils.newDocument();
-    }
-
+    /** The log that is used by this service. */
+    private static Logger log = Logger.getLogger(MainValuesService.class);
 
     @Override
-    public Document doProcess(
-        Document      data,
-        GlobalContext context,
-        CallMeta      callMeta
-    ) {
-        log.debug("MainValuesService.process");
-
-        this.callMeta = callMeta;
-
-        River river = getRequestedRiver(data);
-        if (river == null) {
-            return error("no river found.");
-        }
-
-        double[] minmax = getRequestedStartEnd(data, river);
-        Gauge gauge = river.determineRefGauge(minmax,
-                CMP.compare(minmax[0], minmax[1]) != 0);
-
-        if (gauge == null) {
-            return error("no gauge found.");
-        }
-
-        List<MainValue> mainValues = getMainValues(river, gauge);
-
-        return buildDocument(river, gauge, mainValues, context);
-    }
-
-
-    /**
-     * 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
-    {
-        log.debug("MainValuesService.getRequestedRiver");
-
-        String riverStr = XMLUtils.xpathString(
-            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+    public Document doProcess(final Document data, final GlobalContext context, final CallMeta callMeta) {
+        try {
+            final River river = getRequestedRiver(data);
+            final Gauge gauge = getRequestedGauge(data, river);
 
-         return riverStr != null && (riverStr = riverStr.trim()).length() > 0
-            ? RiverFactory.getRiver(riverStr)
-            : null;
-    }
-
-
-    /**
-     * 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) {
-        log.debug("MainValuesService.getStartEnd");
+            final List<MainValue> mainValues = getMainValues(river, gauge);
 
-        String startStr = XMLUtils.xpathString(
-            data, XPATH_START, ArtifactNamespaceContext.INSTANCE);
-
-        String endStr = XMLUtils.xpathString(
-            data, XPATH_END, ArtifactNamespaceContext.INSTANCE);
-
-        if (startStr == null || endStr == null) {
-            return river.determineMinMaxDistance();
+            return buildDocument(river, gauge, mainValues, context);
         }
-
-        try {
-            double start = Double.parseDouble(startStr);
-            double end   = Double.parseDouble(endStr);
-
-            if (log.isDebugEnabled()) {
-                log.debug("Found start: " + start);
-                log.debug("Found end: " + end);
-            }
-
-            return new double[] { start, end };
-        }
-        catch (NumberFormatException nfe) {
-            log.warn(nfe, nfe);
-            return river.determineMinMaxDistance();
+        catch (final MainValuesServiceException e) {
+            e.printStackTrace();
+            return error(e.getMessage());
         }
     }
 
-
     /**
      * 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.
+     * @param river
+     *            The river.
+     * @param gauge
+     *            The gauge.
      *
      * @return a document that includes the main values of the specified river
-     * at the specified gauge.
+     *         at the specified gauge.
      */
-    protected List<MainValue> getMainValues(River river, Gauge gauge) {
+    protected List<MainValue> getMainValues(final River river, final Gauge gauge) {
 
         if (log.isDebugEnabled()) {
             log.debug("MainValuesService.buildMainValues");
@@ -185,7 +67,7 @@
             log.debug("Gauge: " + gauge.getName());
         }
 
-        List<MainValue> mainValues = gauge.getMainValues();
+        final List<MainValue> mainValues = gauge.getMainValues();
 
         if (log.isDebugEnabled()) {
             log.debug(mainValues.size() + " main values found.");
@@ -193,167 +75,4 @@
 
         return mainValues;
     }
-
-
-    protected Document buildDocument(
-        River           river,
-        Gauge           gauge,
-        List<MainValue> mainValues,
-        Object          context)
-    {
-        log.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, river.getId(), 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)
-    {
-        log.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);
-    }
-
-
-    /** Checks i a main value has an official associated, */
-    protected static boolean hasOfficialLine(
-        NamedMainValue nmv,
-        Integer riverId
-    ) {
-        for (OfficialLine ol: nmv.getOfficialLines()) {
-            if (
-                ol.getWstColumn().getWst().getRiver().getId().equals(riverId)
-            ) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    /** Append xml representation of main values to document. */
-    protected void appendMainValues(
-        Document        doc,
-        Element         root,
-        List<MainValue> mainValues,
-        Integer         riverId,
-        Object          context)
-    {
-        log.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, riverId, 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,
-        Integer   riverId,
-        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());
-        if (mainValue.getTimeInterval() != null) {
-            if (mainValue.getTimeInterval().getStartTime() != null) {
-                cr.addAttr(el, "starttime",
-                    Long.toString(
-                        mainValue.getTimeInterval().getStartTime().getTime()));
-            }
-            if (mainValue.getTimeInterval().getStopTime() != null) {
-                cr.addAttr(el, "stoptime",
-                    Long.toString(
-                        mainValue.getTimeInterval().getStopTime().getTime()));
-            }
-        }
-
-        if (hasOfficialLine(namedMainValue, riverId)) {
-            cr.addAttr(el, "official", "true");
-        }
-
-        return el;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Tue Jul 24 10:39:03 2018 +0200
@@ -1586,4 +1586,8 @@
     String bundu_bezugswst_auto_event_choice();
 
     String years_general();
+
+    String bundu_bezugswst_mode_ud();
+
+    String bundu_bezugswst_validation_range();
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Tue Jul 24 10:39:03 2018 +0200
@@ -846,6 +846,8 @@
 bundu = Betrieb und Unterhaltung 
 bundu_bezugswst_fix_choice_title= Fixierungsauswahl
 bundu_bezugswst_auto_event_choice = Automatische Ereignisauswahl
+bundu_bezugswst_mode_ud = UD am Pegel [d/a] 
+bundu_bezugswst_validation_range = Die Eingabe muss eine Ganzzahl zwischen 0 und 364 sein.
 
 chart_settings_export_metadata = Show Meta-Data
 export_csv_title = Title: 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Tue Jul 24 10:39:03 2018 +0200
@@ -835,7 +835,7 @@
 uinfo_vegetation_zones_from = \u00dcfd von [d/a]
 uinfo_vegetation_zones_to = \u00dcfd bis [d/a]
 uinfo_vegetation_zones_validation_empty = Eingabefeld leer.
-uinfo_vegetation_zones_validation_range = Werte m\u00fcssen zwischen 0 und 365 liegen.
+uinfo_vegetation_zones_validation_range = Werte m\u00fcssen zwischen 0 und 365 liegen. 
 uinfo_vegetation_zones_validation_from_greater_to = Der Wert f\u00fcr "\u00dcfd von" muss kleiner als "\u00dcfd bis". 
 uinfo_salix_km_limit_exceed = Die Zahl ist au\u00dferhalb des g\u00fcltigen km-Bereichs.
 uinfo_salix_km_overlap = Km-Bereiche \u00fcberlappen.
@@ -846,6 +846,8 @@
 bundu = Betrieb und Unterhaltung
 bundu_bezugswst_fix_choice_title= Fixierungsauswahl
 bundu_bezugswst_auto_event_choice = Automatische Ereignisauswahl
+bundu_bezugswst_mode_ud = UD am Pegel [d/a] 
+bundu_bezugswst_validation_range = Die Eingabe muss eine Ganzzahl zwischen 0 und 364 sein.
 
 chart_settings_export_metadata = Metadaten anzeigen
 export_csv_title = Titel: 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/services/DynamicMainValuesService.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,29 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.client.services;
+
+import org.dive4elements.river.client.shared.exceptions.ServerException;
+import org.dive4elements.river.client.shared.model.WQInfoObject;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+/**
+ * This service is used to fetch a list dynamically computed main values.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+@RemoteServiceRelativePath("dynamic-mainvalues")
+public interface DynamicMainValuesService extends RemoteService {
+
+    /**
+     * This method returns a list of DistanceInfoObjects for a specific river.
+     */
+    WQInfoObject[] getWQInfo(String locale, String river, double start, double end, int startYear, int endYear) throws ServerException;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/services/DynamicMainValuesServiceAsync.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,21 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.client.client.services;
+
+import org.dive4elements.river.client.shared.model.WQInfoObject;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * @author Domenico Nardi Tironi
+ */
+public interface DynamicMainValuesServiceAsync {
+    void getWQInfo(String locale, String river, double start, double end, int startYear, int endYear, AsyncCallback<WQInfoObject[]> callback);
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractUIProvider.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractUIProvider.java	Tue Jul 24 10:39:03 2018 +0200
@@ -248,6 +248,7 @@
      * @return the Data with the name <i>name</i>.
      */
     protected Data getData(final List<Data> data, final String name) {
+
         for (final Data d : data) {
             if (name.equals(d.getLabel())) {
                 return d;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractWQAdaptedInputPanel.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,511 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.dive4elements.river.client.client.Config;
+import org.dive4elements.river.client.client.FLYSConstants;
+import org.dive4elements.river.client.client.ui.wq.QDTable;
+import org.dive4elements.river.client.client.ui.wq.WTable;
+import org.dive4elements.river.client.shared.model.ArtifactDescription;
+import org.dive4elements.river.client.shared.model.Data;
+import org.dive4elements.river.client.shared.model.DataItem;
+import org.dive4elements.river.client.shared.model.DataList;
+import org.dive4elements.river.client.shared.model.DefaultData;
+import org.dive4elements.river.client.shared.model.DefaultDataItem;
+import org.dive4elements.river.client.shared.model.WQDataItem;
+import org.dive4elements.river.client.shared.model.WQInfoObject;
+import org.dive4elements.river.client.shared.model.WQInfoRecord;
+
+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.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.events.BlurEvent;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.grid.events.CellClickEvent;
+import com.smartgwt.client.widgets.grid.events.CellClickHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.TabSet;
+
+/**
+ * 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>
+ */
+abstract public class AbstractWQAdaptedInputPanel extends AbstractUIProvider implements ChangeHandler, BlurHandler {
+    private static final long serialVersionUID = -3218827566805476423L;
+
+    /** The message class that provides i18n strings. */
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+    protected static final int ROW_HEIGHT = 20;
+
+    /** List of QDTables in inputhelper section. */
+    protected List<QDTable> qdTables;
+
+    private static final String GAUGE_SEPARATOR = ":";
+
+    private static final String GAUGE_PART_SEPARATOR = ";";
+
+    private static final String VALUE_SEPARATOR = ",";
+
+    /** Stores the input panels related to their keys. */
+    protected Map<String, DoubleArrayPanel> wqranges;
+
+    /** [startkm,endkm] per gauge in selected range. */
+    protected List<Double[]> gaugeRanges;
+
+    /** Stores the min/max values for each q range (gauge). */
+    protected Map<String, double[]> qranges;
+
+    /** The RadioGroupItem that determines the w/q input mode. */
+    protected DynamicForm modes;
+    /** List of doubleArrayPanels shown. */
+    protected final ArrayList<DoubleArrayPanel> doubleArrayPanels = new ArrayList<DoubleArrayPanel>();
+    /** List of wTables in inputhelper section. */
+    protected List<WTable> wTables;
+
+    /** Tabs in inputhelper area. */
+    protected TabSet tabs;
+
+    public AbstractWQAdaptedInputPanel() {
+        this.wqranges = new HashMap<String, DoubleArrayPanel>();
+        this.qranges = new HashMap<String, double[]>();
+        this.wTables = new ArrayList<WTable>();
+        this.qdTables = new ArrayList<QDTable>();
+    }
+
+    /** Inits the helper panel. */
+    // TODO duplicate in WQInputPanel
+    protected void initHelperPanel() {
+        this.tabs = new TabSet();
+        this.tabs.setWidth100();
+        this.tabs.setHeight100();
+
+        // For each gauge, add two tabs with helper tables.
+        createTabs();
+
+        this.helperContainer.addMember(this.tabs);
+    }
+
+    /** Create labels, canvasses, layouts. */
+    @Override
+    public Canvas create(final DataList data) {
+        beforeCreate(data);
+
+        readGaugeRanges(data);
+        initHelperPanel();
+
+        final Canvas submit = getNextButton();
+        final Canvas widget = createWidget(data);
+        final Label label = new Label(this.MSG.wqadaptedTitle());
+
+        label.setHeight(25);
+
+        final VLayout layout = new VLayout();
+        layout.setMembersMargin(10);
+        layout.setWidth(350);
+
+        layout.addMember(label);
+        layout.addMember(widget);
+        layout.addMember(submit);
+
+        fetchWQData();
+
+        initTableListeners();
+
+        afterCreate();
+
+        return layout;
+    }
+
+    protected abstract void createTabs();
+
+    protected abstract void beforeCreate(final DataList data);
+
+    protected abstract void afterCreate();
+
+    /**
+     * Initializes the listeners of the WQD tables.
+     */
+    // TODO dupe from WQInputPanel
+    protected void initTableListeners() {
+        int i = 0;
+        for (final QDTable qdTable : this.qdTables) {
+            // Register listener such that values are filled in on click.
+            final QDTable table = qdTable;
+            final int fi = i;
+            final CellClickHandler handler = new CellClickHandler() {
+                @Override
+                public void onCellClick(final CellClickEvent e) {
+                    if (table.isDisabled() || table.isLocked()) {
+                        return;
+                    }
+
+                    final Record r = e.getRecord();
+                    final double val = r.getAttributeAsDouble("value");
+
+                    AbstractWQAdaptedInputPanel.this.doubleArrayPanels.get(fi).setValues(new double[] { val });
+                    // If a named value for first gauge is chosen,
+                    // try to find and set
+                    // the values to the other panels too.
+                    if (fi == 0) {
+                        final String valueName = r.getAttribute("name");
+                        int oi = 0;
+                        // TODO instead of oi use random access.
+                        for (final QDTable otherQDTable : AbstractWQAdaptedInputPanel.this.qdTables) {
+                            if (oi == 0) {
+                                oi++;
+                                continue;
+                            }
+                            final Double value = otherQDTable.findRecordValue(valueName);
+                            if (value == null) {
+                                SC.warn(AbstractWQAdaptedInputPanel.this.MSG.noMainValueAtGauge());
+                            } else {
+                                AbstractWQAdaptedInputPanel.this.doubleArrayPanels.get(oi).setValues(new double[] { value });
+                            }
+                            oi++;
+                        }
+                    } else {
+                        // Focus next.
+                        if (fi != AbstractWQAdaptedInputPanel.this.doubleArrayPanels.size() - 1) {
+                            AbstractWQAdaptedInputPanel.this.doubleArrayPanels.get(fi + 1).focusInItem(1);
+                        }
+                    }
+                }
+            };
+
+            qdTable.addCellClickHandler(handler);
+            i++;
+        }
+
+        i = 0;
+        for (final WTable wTable : this.wTables) {
+            // Register listener such that values are filled in on click.
+            final WTable table = wTable;
+            final int fi = i;
+            final CellClickHandler handler = new CellClickHandler() {
+
+                @Override
+                public void onCellClick(final CellClickEvent e) {
+                    if (table.isDisabled()) {
+                        return;
+                    }
+
+                    final Record r = e.getRecord();
+                    final double val = r.getAttributeAsDouble("value");
+
+                    AbstractWQAdaptedInputPanel.this.doubleArrayPanels.get(fi).setValues(new double[] { val });
+                    // If a named value for first gauge is chosen,
+                    // try to find and set
+                    // the values to the other panels too.
+                    if (fi == 0) {
+                        final String valueName = r.getAttribute("name");
+                        int oi = 0;
+                        // TODO instead of oi use random access.
+                        for (final WTable otherWTable : AbstractWQAdaptedInputPanel.this.wTables) {
+                            if (oi == 0) {
+                                oi++;
+                                continue;
+                            }
+                            final Double value = otherWTable.findRecordValue(valueName);
+                            if (value == null) {
+                                // TODO: afterwards it freaks out
+                                SC.warn(AbstractWQAdaptedInputPanel.this.MSG.noMainValueAtGauge());
+                            } else {
+                                AbstractWQAdaptedInputPanel.this.doubleArrayPanels.get(oi).setValues(new double[] { value });
+                            }
+                            oi++;
+                        }
+                    } else {
+                        // Focus next.
+                        if (fi != AbstractWQAdaptedInputPanel.this.doubleArrayPanels.size() - 1) {
+                            AbstractWQAdaptedInputPanel.this.doubleArrayPanels.get(fi + 1).focusInItem(1);
+                        }
+                    }
+                }
+
+            };
+
+            wTable.addCellClickHandler(handler);
+            i++;
+        }
+    }
+
+    /** Create area showing previously entered w or q data. */
+    protected final Canvas createOldWQValues(final Data wqData, final boolean isQ) {
+
+        final VLayout layout = new VLayout();
+        if (wqData != null) {
+            final DataItem item = wqData.getItems()[0];
+            final String value = item.getStringValue();
+
+            final String[] gauges = value.split(GAUGE_SEPARATOR);
+
+            final String unit = isQ ? "m³/s" : "cm";
+
+            for (final String gauge : gauges) {
+                final HLayout h = new HLayout();
+
+                final String[] parts = gauge.split(GAUGE_PART_SEPARATOR);
+                final String[] values = parts[3].split(VALUE_SEPARATOR);
+
+                final Label l = new Label(parts[2] + ": ");
+
+                final StringBuilder sb = new StringBuilder();
+                boolean first = true;
+
+                for (final String v : values) {
+                    if (!first) {
+                        sb.append(", ");
+                    }
+
+                    sb.append(v);
+                    sb.append(" ");
+                    sb.append(unit);
+
+                    first = false;
+                }
+
+                final Label v = new Label(sb.toString());
+
+                l.setWidth(65);
+                v.setWidth(65);
+
+                h.addMember(l);
+                h.addMember(v);
+
+                layout.addMember(h);
+            }
+        }
+
+        return layout;
+    }
+
+    protected void initUserWQValues(final DataList dataList) {
+        final List<Data> allData = dataList.getAll();
+
+        final Data dDef = getData(allData, "wq_values");
+        final DataItem def = dDef != null ? dDef.getDefault() : null;
+        final String value = def != null ? def.getStringValue() : null;
+
+        if (value == null || value.length() == 0) {
+            return;
+        }
+
+        final String[] lines = value.split(GAUGE_SEPARATOR);
+
+        if (lines == null || lines.length == 0) {
+            return;
+        }
+
+        for (final String line : lines) {
+            final String[] cols = line.split(GAUGE_PART_SEPARATOR);
+            // final String title = createLineTitle(line);
+
+            if (cols == null || cols.length < 3) {
+                continue;
+            }
+
+            final String[] strValues = cols[2].split(VALUE_SEPARATOR);
+            final double[] values = new double[strValues.length];
+
+            int idx = 0;
+
+            for (final String strValue : strValues) {
+                try {
+                    values[idx++] = Double.valueOf(strValue);
+                }
+                catch (final NumberFormatException nfe) {
+                    // do nothing
+                }
+            }
+
+            final String key = cols[0] + GAUGE_PART_SEPARATOR + cols[1];
+            final DoubleArrayPanel dap = this.wqranges.get(key);
+
+            if (dap == null) {
+                continue;
+            }
+
+            dap.setValues(values);
+        }
+    }
+
+    /** Populate Gauge Ranges array. */
+    protected final void readGaugeRanges(final DataList dataList) {
+        final List<WQDataItem> items = getWQItems(dataList);
+        this.gaugeRanges = new ArrayList<Double[]>();
+
+        for (final DataItem item : items) {
+            if (item instanceof WQDataItem) {
+                final String[] startEndKm = item.getLabel().split(";");
+                final Double[] kvp = new Double[] { Double.parseDouble(startEndKm[0]), Double.parseDouble(startEndKm[1]) };
+                this.gaugeRanges.add(kvp);
+            }
+        }
+    }
+
+    /** Get items which are not WQ_MODE. */
+    protected List<WQDataItem> getWQItems(final DataList dataList) {
+        final List<Data> data = dataList.getAll();
+        final List<WQDataItem> results = new ArrayList<WQDataItem>();
+        for (final Data d : data) {
+            final DataItem[] items = d.getItems();
+            if (items != null) {
+                for (final Object item : items) {
+                    if (item instanceof WQDataItem)
+                        results.add((WQDataItem) item);
+                }
+            }
+        }
+        return results;
+    }
+
+    protected final Data getWQValues(final String dataKey) {
+        String wqvalue = null;
+
+        for (final Map.Entry<String, DoubleArrayPanel> entry : this.wqranges.entrySet()) {
+            final String key = entry.getKey();
+            final DoubleArrayPanel dap = entry.getValue();
+            final String label = dap.getItemTitle();
+
+            final double[] values = dap.getInputValues();
+            if (wqvalue == null) {
+                wqvalue = createValueString(key + ";" + label, values);
+            } else {
+                wqvalue += GAUGE_SEPARATOR + createValueString(key + ";" + label, values);
+            }
+        }
+
+        final DataItem valueItem = new DefaultDataItem(dataKey, dataKey, wqvalue);
+        final Data values = new DefaultData(dataKey, null, null, new DataItem[] { valueItem });
+
+        return values;
+    }
+
+    protected String createValueString(final String key, final double[] values) {
+        final StringBuilder sb = new StringBuilder();
+
+        boolean first = true;
+
+        for (final double value : values) {
+            if (!first) {
+                sb.append(",");
+            }
+
+            sb.append(Double.toString(value));
+
+            first = false;
+        }
+
+        return key + ";" + sb.toString();
+    }
+
+    @Override
+    public void onChange(final ChangeEvent event) {
+        // TODO IMPLEMENT ME
+    }
+
+    /** Store the currently focussed DoubleArrayPanel and focus helper tab. */
+
+    @Override
+    public final void onBlur(final BlurEvent event) {
+        final DoubleArrayPanel dap = (DoubleArrayPanel) event.getForm();
+        dap.validateForm(event.getItem());
+    }
+
+    /** Get the WQD data from service and stuck them up that tables. */
+    protected void fetchWQData() {
+        final Config config = Config.getInstance();
+        final String locale = config.getLocale();
+
+        final ArtifactDescription adescr = this.artifact.getArtifactDescription();
+        final DataList[] data = adescr.getOldData();
+
+        final String river = getRiverName(data);
+
+        int i = 0;
+
+        // Get Data for respective gauge.
+        for (final Double[] range : this.gaugeRanges) {
+            // Gauge ranges overlap, move start and end a bit closer
+            // to each other.
+            final double rDiff = (range[1] - range[0]) / 10d;
+            final int fi = i;
+
+            final AsyncCallback<WQInfoObject[]> cb = new AsyncCallback<WQInfoObject[]>() {
+                @Override
+                public void onFailure(final Throwable caught) {
+                    GWT.log("Could not recieve wq informations.");
+                    SC.warn(caught.getMessage());
+                }
+
+                @Override
+                public void onSuccess(final WQInfoObject[] wqi) {
+                    final int num = wqi != null ? wqi.length : 0;
+                    GWT.log("Received " + num + " wq informations (" + fi + ".");
+
+                    if (num == 0) {
+                        return;
+                    }
+
+                    addWQInfo(wqi, fi);
+                }
+            };
+
+            callMainValuesService(locale, river, range[0] + rDiff, range[1] - rDiff, cb);
+
+            i++;
+        }
+    }
+
+    protected abstract void callMainValuesService(String locale, String river, double start, double end, AsyncCallback<WQInfoObject[]> cb);
+
+    /** Add Info to helper table for gauge at index gaugeIdx. */
+    private void addWQInfo(final WQInfoObject[] wqi, final int gaugeIdx) {
+        for (final WQInfoObject wi : wqi) {
+            final WQInfoRecord rec = new WQInfoRecord(wi);
+
+            if (wi.getType().equals("W")) {
+                if (gaugeIdx < this.wTables.size())
+                    this.wTables.get(gaugeIdx).addData(rec);
+            } else if (wi.getType().equals("Q")) {
+                if (gaugeIdx < this.qdTables.size())
+                    this.qdTables.get(gaugeIdx).addData(rec);
+            }
+        }
+    }
+
+    /**
+     * Returns the name of the selected river.
+     *
+     * @param data
+     *            The DataList with all data.
+     *
+     * @return the name of the current river.
+     */
+    protected String getRiverName(final DataList[] data) {
+        final ArtifactDescription adesc = this.artifact.getArtifactDescription();
+        return adesc.getRiver();
+    }
+
+    abstract protected Canvas createWidget(final DataList dataList);
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WQAdaptedInputPanel.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WQAdaptedInputPanel.java	Tue Jul 24 10:39:03 2018 +0200
@@ -14,13 +14,10 @@
 import java.util.List;
 import java.util.Map;
 
-import org.dive4elements.river.client.client.Config;
-import org.dive4elements.river.client.client.FLYSConstants;
 import org.dive4elements.river.client.client.services.WQInfoService;
 import org.dive4elements.river.client.client.services.WQInfoServiceAsync;
 import org.dive4elements.river.client.client.ui.wq.QDTable;
 import org.dive4elements.river.client.client.ui.wq.WTable;
-import org.dive4elements.river.client.shared.model.ArtifactDescription;
 import org.dive4elements.river.client.shared.model.Data;
 import org.dive4elements.river.client.shared.model.DataItem;
 import org.dive4elements.river.client.shared.model.DataList;
@@ -28,12 +25,10 @@
 import org.dive4elements.river.client.shared.model.DefaultDataItem;
 import org.dive4elements.river.client.shared.model.WQDataItem;
 import org.dive4elements.river.client.shared.model.WQInfoObject;
-import org.dive4elements.river.client.shared.model.WQInfoRecord;
 
 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.types.TitleOrientation;
 import com.smartgwt.client.types.VerticalAlignment;
 import com.smartgwt.client.util.SC;
@@ -41,18 +36,13 @@
 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.BlurEvent;
-import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
 import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
 import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
 import com.smartgwt.client.widgets.form.fields.events.FocusEvent;
 import com.smartgwt.client.widgets.form.fields.events.FocusHandler;
-import com.smartgwt.client.widgets.grid.events.CellClickEvent;
-import com.smartgwt.client.widgets.grid.events.CellClickHandler;
 import com.smartgwt.client.widgets.layout.HLayout;
 import com.smartgwt.client.widgets.layout.VLayout;
 import com.smartgwt.client.widgets.tab.Tab;
-import com.smartgwt.client.widgets.tab.TabSet;
 
 /**
  * This UIProvider creates a widget to enter W or Q data for discharge
@@ -60,257 +50,204 @@
  *
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
-public class WQAdaptedInputPanel extends AbstractUIProvider implements ChangeHandler, BlurHandler, FocusHandler {
+public class WQAdaptedInputPanel extends AbstractWQAdaptedInputPanel implements FocusHandler {
     private static final long serialVersionUID = -3218827566805476423L;
-
-    /** The message class that provides i18n strings. */
-    protected FLYSConstants MESSAGE = GWT.create(FLYSConstants.class);
-
-    public static final String FIELD_WQ_MODE = "wq_isq";
-    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 constant field name for choosing w or q mode. */
-    public static final String FIELD_WQ = "wq";
-
-    /** The constant field name for choosing single values or range. */
-    public static final String FIELD_MODE = "mode";
-
-    /** The constant field value for range input mode. */
-    public static final String FIELD_MODE_RANGE = "range";
-
-    /** Service to fetch W/Q MainValues. */
-    protected WQInfoServiceAsync wqInfoService = GWT.create(WQInfoService.class);
-
-    /** 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;
-
-    /** List of doubleArrayPanels shown. */
-    protected ArrayList<DoubleArrayPanel> doubleArrayPanels;
-
-    /** [startkm,endkm] per gauge in selected range. */
-    protected List<Double[]> gaugeRanges;
-
-    /** Stores the min/max values for each q range (gauge). */
-    protected Map<String, double[]> qranges;
+    private static final String FIELD_WQ_MODE = "wq_isq";
+    private static final String FIELD_WQ_W = "W";
+    private static final String FIELD_WQ_Q = "Q";
 
     /** Stores the min/max values for each w range (gauge). */
-    protected Map<String, double[]> wranges;
-
-    /** The RadioGroupItem that determines the w/q input mode. */
-    protected DynamicForm modes;
+    private final Map<String, double[]> wranges = new HashMap<String, double[]>();
 
-    /** List of wTables in inputhelper section. */
-    protected List<WTable> wTables;
-
-    /** List of QDTables in inputhelper section. */
-    protected List<QDTable> qdTables;
-
-    /** Tabs in inputhelper area. */
-    protected TabSet tabs;
+    /** Service to fetch W/Q MainValues. */
+    private final WQInfoServiceAsync wqInfoService = GWT.create(WQInfoService.class);
 
     /** The currently focussed Input element. */
-    protected DoubleArrayPanel itemWithFocus;
+    private DoubleArrayPanel itemWithFocus;
 
-    public WQAdaptedInputPanel() {
-        this.wqranges = new HashMap<String, DoubleArrayPanel>();
-        this.doubleArrayPanels = new ArrayList<DoubleArrayPanel>();
-        this.qranges = new HashMap<String, double[]>();
-        this.wranges = new HashMap<String, double[]>();
-        this.wTables = new ArrayList<WTable>();
-        this.qdTables = new ArrayList<QDTable>();
+    private void enableWTabs() {
+        for (int i = 0; i < this.doubleArrayPanels.size(); i++) {
+            this.tabs.disableTab(2 * i);
+            this.tabs.enableTab(2 * i + 1);
+        }
     }
 
-    /** Create labels, canvasses, layouts. */
-    @Override
-    public Canvas create(final DataList data) {
-        readGaugeRanges(data);
-        initHelperPanel();
-
-        final Canvas submit = getNextButton();
-        final Canvas widget = createWidget(data);
-        final Label label = new Label(this.MSG.wqadaptedTitle());
-
-        label.setHeight(25);
+    private void enableQTabs() {
+        for (int i = 0; i < this.doubleArrayPanels.size(); i++) {
+            this.tabs.enableTab(2 * i);
+            this.tabs.disableTab(2 * i + 1);
+        }
+    }
 
-        final VLayout layout = new VLayout();
-        layout.setMembersMargin(10);
-        layout.setWidth(350);
+    /**
+     * Create radio button for switching w and q input.
+     * Radiobutton-change also triggers helper panel tab selection.
+     */
 
-        layout.addMember(label);
-        layout.addMember(widget);
-        layout.addMember(submit);
+    private final Canvas createMode(final DataList dataList) {
+        final RadioGroupItem wq = new RadioGroupItem(FIELD_WQ_MODE);
+        wq.setShowTitle(false);
+        wq.setVertical(false);
+        wq.setWidth(200);
 
-        fetchWQData();
+        final LinkedHashMap<String, String> wqValues = new LinkedHashMap<String, String>();
+        wqValues.put(FIELD_WQ_W, this.MSG.wqW());
+        wqValues.put(WQAdaptedInputPanel.FIELD_WQ_Q, this.MSG.wqQatGauge());
 
-        initTableListeners();
+        wq.setValueMap(wqValues);
 
+        this.modes = new DynamicForm();
+        this.modes.setFields(wq);
+        this.modes.setWidth(200);
+        wq.addChangeHandler(new ChangeHandler() {
+            @Override
+            public void onChange(final ChangeEvent e) {
+                final DynamicForm form = e.getForm();
+
+                if (form.getValueAsString(FIELD_WQ_MODE).contains("Q")) {
+                    WQAdaptedInputPanel.this.tabs.selectTab(0);
+                    enableQTabs();
+                } else {
+                    WQAdaptedInputPanel.this.tabs.selectTab(1);
+                    enableWTabs();
+                }
+            }
+        });
+
+        final LinkedHashMap<String, String> initial = new LinkedHashMap<String, String>();
+        initial.put(FIELD_WQ_MODE, this.FIELD_WQ_Q);
+        this.modes.setValues(initial);
+        this.tabs.selectTab(1);
+        return this.modes;
+    }
+
+    @Override
+    protected void createTabs() {
+        for (int i = 0; i < this.gaugeRanges.size(); i++) {
+            // Later the tabs title will get adjusted to include gauges name.
+            // TODO the tabs title becomes rather long through that (i18n).
+
+            final Tab wTab = new Tab(this.MSG.wq_table_w());
+            final WTable wTable = new WTable();
+            this.wTables.add(wTable);
+            wTable.showSelect();
+            wTab.setPane(wTable);
+
+            final Tab qTab = new Tab(this.MSG.wq_table_q());
+            final QDTable qdTable = new QDTable();
+            this.qdTables.add(qdTable);
+            qTab.setPane(qdTable);
+            qdTable.showSelect();
+
+            this.tabs.addTab(wTab, i * 2 + 0);
+            this.tabs.addTab(qTab, i * 2 + 1);
+
+        }
+
+        // Defaults at "Q", first input field.
+        this.tabs.selectTab(0);
+        enableQTabs();
+    }
+
+    @Override
+    protected void beforeCreate(final DataList data) {
+    }
+
+    @Override
+    protected void afterCreate() {
         // We actually want the first Q tab to be selected and all
         // Q tabs to be enabled. I sense a bug in TabSet here, as
         // the code in the W/Q radiogroup-changehandler behaves
         // exactly vice versa (enabling Q, selecting tab 0).
         enableWTabs();
         this.tabs.selectTab(1);
+    }
+
+    @Override
+    protected Canvas createWidget(final DataList dataList) {
+        final VLayout layout = new VLayout();
+        final Canvas mode = createMode(dataList);
+        final Canvas list = createList(dataList);
+
+        final List<WQDataItem> items = getWQItems(dataList);
+        final int listHeight = ROW_HEIGHT * items.size();
+
+        mode.setHeight(25);
+        mode.setWidth(200);
+
+        layout.addMember(mode);
+        layout.addMember(list);
+
+        layout.setHeight(25 + listHeight);
+        layout.setWidth(350);
+
+        initUserDefaults(dataList);
 
         return layout;
     }
 
-    /** Inits the helper panel. */
-    // TODO duplicate in WQInputPanel
-    protected void initHelperPanel() {
-        this.tabs = new TabSet();
-        this.tabs.setWidth100();
-        this.tabs.setHeight100();
-
-        // For each gauge, add two tabs with helper tables.
-
-        for (int i = 0; i < this.gaugeRanges.size(); i++) {
-            // Later the tabs title will get adjusted to include gauges name.
-            // TODO the tabs title becomes rather long through that (i18n).
-            final Tab wTab = new Tab(this.MESSAGE.wq_table_w());
-            final Tab qTab = new Tab(this.MESSAGE.wq_table_q());
-
-            final QDTable qdTable = new QDTable();
-            final WTable wTable = new WTable();
-
-            this.wTables.add(wTable);
-            this.qdTables.add(qdTable);
-
-            qdTable.showSelect();
-            wTable.showSelect();
-            wTab.setPane(wTable);
-            qTab.setPane(qdTable);
-
-            this.tabs.addTab(wTab, i * 2 + 0);
-            this.tabs.addTab(qTab, i * 2 + 1);
-            // tabs.disableTab(i*2+1);
-        }
-
-        // Defaults at "Q", first input field.
-        this.tabs.selectTab(0);
-        enableQTabs();
-
-        this.helperContainer.addMember(this.tabs);
+    private void initUserDefaults(final DataList dataList) {
+        initUserWQValues(dataList);
+        initUserWQMode(dataList);
     }
 
-    /**
-     * Initializes the listeners of the WQD tables.
-     */
-    // TODO dupe from WQInputPanel
-    protected void initTableListeners() {
-        int i = 0;
-        for (final QDTable qdTable : this.qdTables) {
-            // Register listener such that values are filled in on click.
-            final QDTable table = qdTable;
-            final int fi = i;
-            final CellClickHandler handler = new CellClickHandler() {
-                @Override
-                public void onCellClick(final CellClickEvent e) {
-                    if (isWMode() || table.isLocked()) {
-                        return;
-                    }
-
-                    final Record r = e.getRecord();
-                    final double val = r.getAttributeAsDouble("value");
-
-                    WQAdaptedInputPanel.this.doubleArrayPanels.get(fi).setValues(new double[] { val });
-                    // If a named value for first gauge is chosen,
-                    // try to find and set
-                    // the values to the other panels too.
-                    if (fi == 0) {
-                        final String valueName = r.getAttribute("name");
-                        int oi = 0;
-                        // TODO instead of oi use random access.
-                        for (final QDTable otherQDTable : WQAdaptedInputPanel.this.qdTables) {
-                            if (oi == 0) {
-                                oi++;
-                                continue;
-                            }
-                            final Double value = otherQDTable.findRecordValue(valueName);
-                            if (value == null) {
-                                SC.warn(WQAdaptedInputPanel.this.MSG.noMainValueAtGauge());
-                            } else {
-                                WQAdaptedInputPanel.this.doubleArrayPanels.get(oi).setValues(new double[] { value });
-                            }
-                            oi++;
-                        }
-                    } else {
-                        // Focus next.
-                        if (fi != WQAdaptedInputPanel.this.doubleArrayPanels.size() - 1) {
-                            WQAdaptedInputPanel.this.doubleArrayPanels.get(fi + 1).focusInItem(1);
-                        }
-                    }
-                }
-            };
+    private void initUserWQMode(final DataList dataList) {
+        final List<Data> allData = dataList.getAll();
 
-            qdTable.addCellClickHandler(handler);
-            i++;
-        }
-
-        i = 0;
-        for (final WTable wTable : this.wTables) {
-            // Register listener such that values are filled in on click.
-            final WTable table = wTable;
-            final int fi = i;
-            final CellClickHandler handler = new CellClickHandler() {
-                @Override
-                public void onCellClick(final CellClickEvent e) {
-                    if (!isWMode() /* || table.isLocked() */) {
-                        return;
-                    }
-
-                    final Record r = e.getRecord();
-                    final double val = r.getAttributeAsDouble("value");
+        final Data dDef = getData(allData, "wq_mode");
+        final DataItem def = dDef != null ? dDef.getDefault() : null;
+        final String value = def != null ? def.getStringValue() : null;
 
-                    WQAdaptedInputPanel.this.doubleArrayPanels.get(fi).setValues(new double[] { val });
-                    // If a named value for first gauge is chosen,
-                    // try to find and set
-                    // the values to the other panels too.
-                    if (fi == 0) {
-                        final String valueName = r.getAttribute("name");
-                        int oi = 0;
-                        // TODO instead of oi use random access.
-                        for (final WTable otherWTable : WQAdaptedInputPanel.this.wTables) {
-                            if (oi == 0) {
-                                oi++;
-                                continue;
-                            }
-                            final Double value = otherWTable.findRecordValue(valueName);
-                            if (value == null) {
-                                // TODO: afterwards it freaks out
-                                SC.warn(WQAdaptedInputPanel.this.MSG.noMainValueAtGauge());
-                            } else {
-                                WQAdaptedInputPanel.this.doubleArrayPanels.get(oi).setValues(new double[] { value });
-                            }
-                            oi++;
-                        }
-                    } else {
-                        // Focus next.
-                        if (fi != WQAdaptedInputPanel.this.doubleArrayPanels.size() - 1) {
-                            WQAdaptedInputPanel.this.doubleArrayPanels.get(fi + 1).focusInItem(1);
-                        }
-                    }
-                }
-            };
-
-            wTable.addCellClickHandler(handler);
-            i++;
+        if (value != null && value.equals(FIELD_WQ_W)) {
+            this.modes.setValue(FIELD_WQ_MODE, FIELD_WQ_W);
+        } else {
+            this.modes.setValue(FIELD_WQ_MODE, FIELD_WQ_Q);
         }
     }
 
+    private Canvas createList(final DataList dataList) {
+        final VLayout layout = new VLayout();
+
+        final List<WQDataItem> items = getWQItems(dataList);
+
+        int i = 0;
+
+        for (final DataItem item : items) {
+            if (item instanceof WQDataItem) {
+                final String title = item.getLabel(); // of form: 70.5;112.0
+                final String label = item.getStringValue();
+
+                // Rename W and Q tab to include gauges name.
+                this.tabs.getTab(i * 2).setTitle(this.tabs.getTab(i * 2).getTitle() + " (" + label + ")");
+                this.tabs.getTab(i * 2 + 1).setTitle(this.tabs.getTab(i * 2 + 1).getTitle() + " (" + label + ")");
+
+                final DoubleArrayPanel dap = new DoubleArrayPanel(label, null, this, this, TitleOrientation.LEFT);
+
+                this.wqranges.put(title, dap);
+                this.doubleArrayPanels.add(dap);
+
+                if (item instanceof WQDataItem) {
+                    final WQDataItem wq = (WQDataItem) item;
+                    final double[] mmQ = wq.getQRange();
+                    final double[] mmW = wq.getWRange();
+
+                    this.qranges.put(title, mmQ);
+                    this.wranges.put(title, mmW);
+                }
+
+                layout.addMember(dap);
+                i++;
+            }
+
+        }
+
+        layout.setHeight(items.size() * ROW_HEIGHT);
+
+        return layout;
+    }
+
     @Override
-    public Canvas createOld(final DataList dataList) {
+    public Canvas createOld(final DataList dataList) { // TODO: OVERRIDE
         final List<Data> all = dataList.getAll();
         final Data wqData = getData(all, "wq_values");
         final Data wqMode = getData(all, "wq_isq");
@@ -343,78 +280,6 @@
         return vlayout;
     }
 
-    /** Create area showing previously entered w or q data. */
-    protected Canvas createOldWQValues(final Data wqData, final boolean isQ) {
-        final VLayout layout = new VLayout();
-
-        final DataItem item = wqData.getItems()[0];
-        final String value = item.getStringValue();
-
-        final String[] gauges = value.split(GAUGE_SEPARATOR);
-
-        final String unit = isQ ? "m³/s" : "cm";
-
-        for (final String gauge : gauges) {
-            final HLayout h = new HLayout();
-
-            final String[] parts = gauge.split(GAUGE_PART_SEPARATOR);
-            final String[] values = parts[3].split(VALUE_SEPARATOR);
-
-            final Label l = new Label(parts[2] + ": ");
-
-            final StringBuilder sb = new StringBuilder();
-            boolean first = true;
-
-            for (final String v : values) {
-                if (!first) {
-                    sb.append(", ");
-                }
-
-                sb.append(v);
-                sb.append(" ");
-                sb.append(unit);
-
-                first = false;
-            }
-
-            final Label v = new Label(sb.toString());
-
-            l.setWidth(65);
-            v.setWidth(65);
-
-            h.addMember(l);
-            h.addMember(v);
-
-            layout.addMember(h);
-        }
-
-        return layout;
-    }
-
-    /** Create non-input helper part of the UI. */
-    protected Canvas createWidget(final DataList dataList) {
-        final VLayout layout = new VLayout();
-
-        final Canvas mode = createMode(dataList);
-        final Canvas list = createList(dataList);
-
-        final DataItem[] items = getWQItems(dataList);
-        final 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()) {
@@ -424,6 +289,12 @@
         }
     }
 
+    private boolean isWMode() {
+        final String mode = (String) this.modes.getValue(FIELD_WQ_MODE);
+
+        return FIELD_WQ_W.equals(mode);
+    }
+
     protected List<String> validateRange(final Map<String, double[]> ranges) {
         final List<String> errors = new ArrayList<String>();
         final NumberFormat nf = NumberFormat.getDecimalFormat();
@@ -477,233 +348,7 @@
         return errors;
     }
 
-    protected List<String> validateW() {
-        return validateRange(this.wranges);
-    }
-
-    protected List<String> validateQ() {
-        return validateRange(this.qranges);
-    }
-
-    protected void initUserDefaults(final DataList dataList) {
-        initUserWQValues(dataList);
-        initUserWQMode(dataList);
-    }
-
-    protected void initUserWQMode(final DataList dataList) {
-        final List<Data> allData = dataList.getAll();
-
-        final Data dDef = getData(allData, "wq_mode");
-        final DataItem def = dDef != null ? dDef.getDefault() : null;
-        final String value = def != null ? def.getStringValue() : null;
-
-        if (value != null && value.equals(FIELD_WQ_W)) {
-            this.modes.setValue(FIELD_WQ_MODE, FIELD_WQ_W);
-        } else {
-            this.modes.setValue(FIELD_WQ_MODE, FIELD_WQ_Q);
-        }
-    }
-
-    protected void initUserWQValues(final DataList dataList) {
-        final List<Data> allData = dataList.getAll();
-
-        final Data dDef = getData(allData, "wq_values");
-        final DataItem def = dDef != null ? dDef.getDefault() : null;
-        final String value = def != null ? def.getStringValue() : null;
-
-        if (value == null || value.length() == 0) {
-            return;
-        }
-
-        final String[] lines = value.split(GAUGE_SEPARATOR);
-
-        if (lines == null || lines.length == 0) {
-            return;
-        }
-
-        for (final String line : lines) {
-            final String[] cols = line.split(GAUGE_PART_SEPARATOR);
-            final String title = createLineTitle(line);
-
-            if (cols == null || cols.length < 3) {
-                continue;
-            }
-
-            final String[] strValues = cols[2].split(VALUE_SEPARATOR);
-            final double[] values = new double[strValues.length];
-
-            int idx = 0;
-
-            for (final String strValue : strValues) {
-                try {
-                    values[idx++] = Double.valueOf(strValue);
-                }
-                catch (final NumberFormatException nfe) {
-                    // do nothing
-                }
-            }
-
-            final String key = cols[0] + GAUGE_PART_SEPARATOR + cols[1];
-            final DoubleArrayPanel dap = this.wqranges.get(key);
-
-            if (dap == null) {
-                continue;
-            }
-
-            dap.setValues(values);
-        }
-    }
-
-    /** Populate Gauge Ranges array. */
-    private void readGaugeRanges(final DataList dataList) {
-        final DataItem[] items = getWQItems(dataList);
-        this.gaugeRanges = new ArrayList<Double[]>();
-
-        int i = 0;
-
-        for (final DataItem item : items) {
-            if (item instanceof WQDataItem) {
-                final String[] startEndKm = item.getLabel().split(";");
-                final Double[] kvp = new Double[] { Double.parseDouble(startEndKm[0]), Double.parseDouble(startEndKm[1]) };
-                this.gaugeRanges.add(kvp);
-            }
-            i++;
-        }
-    }
-
-    protected Canvas createList(final DataList dataList) {
-        final VLayout layout = new VLayout();
-
-        final DataItem[] items = getWQItems(dataList);
-
-        int i = 0;
-
-        for (final DataItem item : items) {
-            if (item instanceof WQDataItem) {
-                final String title = item.getLabel(); // of form: 70.5;112.0
-                final String label = item.getStringValue();
-
-                // Rename W and Q tab to include gauges name.
-                this.tabs.getTab(i * 2).setTitle(this.tabs.getTab(i * 2).getTitle() + " (" + label + ")");
-                this.tabs.getTab(i * 2 + 1).setTitle(this.tabs.getTab(i * 2 + 1).getTitle() + " (" + label + ")");
-
-                final DoubleArrayPanel dap = new DoubleArrayPanel(label, null, this, this, TitleOrientation.LEFT);
-
-                this.wqranges.put(title, dap);
-                this.doubleArrayPanels.add(dap);
-
-                if (item instanceof WQDataItem) {
-                    final WQDataItem wq = (WQDataItem) item;
-                    final double[] mmQ = wq.getQRange();
-                    final double[] mmW = wq.getWRange();
-
-                    this.qranges.put(title, mmQ);
-                    this.wranges.put(title, mmW);
-                }
-
-                layout.addMember(dap);
-            }
-            i++;
-        }
-
-        layout.setHeight(items.length * ROW_HEIGHT);
-
-        return layout;
-    }
-
-    /** Get items which are not WQ_MODE. */
-    protected DataItem[] getWQItems(final DataList dataList) {
-        final List<Data> data = dataList.getAll();
-
-        for (final Data d : data) {
-            final String name = d.getLabel();
-
-            if (name.equals(FIELD_WQ_MODE) || name.startsWith("ignore_")) {
-                continue;
-            }
-
-            return d.getItems();
-        }
-
-        return null;
-    }
-
-    /**
-     * Create radio button for switching w and q input.
-     * Radiobutton-change also triggers helper panel tab selection.
-     */
-    protected Canvas createMode(final DataList dataList) {
-        final RadioGroupItem wq = new RadioGroupItem(FIELD_WQ_MODE);
-        wq.setShowTitle(false);
-        wq.setVertical(false);
-        wq.setWidth(200);
-
-        final LinkedHashMap wqValues = new LinkedHashMap();
-        wqValues.put(FIELD_WQ_W, this.MSG.wqW());
-        wqValues.put(FIELD_WQ_Q, this.MSG.wqQatGauge());
-
-        wq.setValueMap(wqValues);
-
-        this.modes = new DynamicForm();
-        this.modes.setFields(wq);
-        this.modes.setWidth(200);
-        wq.addChangeHandler(new ChangeHandler() {
-            @Override
-            public void onChange(final ChangeEvent e) {
-                final DynamicForm form = e.getForm();
-
-                if (form.getValueAsString(FIELD_WQ_MODE).contains("Q")) {
-                    WQAdaptedInputPanel.this.tabs.selectTab(0);
-                    enableQTabs();
-                } else {
-                    WQAdaptedInputPanel.this.tabs.selectTab(1);
-                    enableWTabs();
-                }
-            }
-        });
-
-        final LinkedHashMap initial = new LinkedHashMap();
-        initial.put(FIELD_WQ_MODE, FIELD_WQ_Q);
-        this.modes.setValues(initial);
-        this.tabs.selectTab(1);
-        return this.modes;
-    }
-
-    public void enableWTabs() {
-        for (int i = 0; i < this.doubleArrayPanels.size(); i++) {
-            this.tabs.disableTab(2 * i);
-            this.tabs.enableTab(2 * i + 1);
-        }
-    }
-
-    public void enableQTabs() {
-        for (int i = 0; i < this.doubleArrayPanels.size(); i++) {
-            this.tabs.enableTab(2 * i);
-            this.tabs.disableTab(2 * i + 1);
-        }
-    }
-
-    public String createLineTitle(final String key) {
-        final String[] splitted = key.split(";");
-
-        return splitted[0] + " - " + splitted[1];
-    }
-
-    @Override
-    public Data[] getData() {
-        final Data mode = getWQMode();
-        final Data values = getWQValues();
-
-        return new Data[] { mode, values };
-    }
-
-    public boolean isWMode() {
-        final String mode = (String) this.modes.getValue(FIELD_WQ_MODE);
-
-        return FIELD_WQ_W.equals(mode);
-    }
-
-    protected Data getWQMode() {
+    private Data getWQMode() {
         final String wqMode = this.modes.getValueAsString(FIELD_WQ_MODE);
         String value = "false";
         if (wqMode.equals("Q")) {
@@ -715,52 +360,22 @@
         return mode;
     }
 
-    protected Data getWQValues() {
-        String wqvalue = null;
-
-        for (final Map.Entry<String, DoubleArrayPanel> entry : this.wqranges.entrySet()) {
-            final String key = entry.getKey();
-            final DoubleArrayPanel dap = entry.getValue();
-            final String label = dap.getItemTitle();
+    @Override
+    public Data[] getData() {
+        final Data mode = getWQMode();
+        final Data values = getWQValues("wq_values");
 
-            final double[] values = dap.getInputValues();
-            if (wqvalue == null) {
-                wqvalue = createValueString(key + ";" + label, values);
-            } else {
-                wqvalue += GAUGE_SEPARATOR + createValueString(key + ";" + label, values);
-            }
-        }
-
-        final DataItem valueItem = new DefaultDataItem("wq_values", "wq_values", wqvalue);
-        final Data values = new DefaultData("wq_values", null, null, new DataItem[] { valueItem });
-
-        return values;
+        return new Data[] { mode, values };
     }
 
-    protected String createValueString(final String key, final double[] values) {
-        final StringBuilder sb = new StringBuilder();
-
-        boolean first = true;
-
-        for (final double value : values) {
-            if (!first) {
-                sb.append(",");
-            }
-
-            sb.append(Double.toString(value));
-
-            first = false;
-        }
-
-        return key + ";" + sb.toString();
+    private List<String> validateW() {
+        return validateRange(this.wranges);
     }
 
-    @Override
-    public void onChange(final ChangeEvent event) {
-        // TODO IMPLEMENT ME
+    private List<String> validateQ() {
+        return validateRange(this.qranges);
     }
 
-    /** Store the currently focussed DoubleArrayPanel and focus helper tab. */
     @Override
     public void onFocus(final FocusEvent event) {
         this.itemWithFocus = (DoubleArrayPanel) event.getForm();
@@ -771,93 +386,8 @@
     }
 
     @Override
-    public void onBlur(final BlurEvent event) {
-        final DoubleArrayPanel dap = (DoubleArrayPanel) event.getForm();
-        dap.validateForm(event.getItem());
+    protected void callMainValuesService(final String locale, final String river, final double start, final double end,
+            final AsyncCallback<WQInfoObject[]> cb) {
+        this.wqInfoService.getWQInfo(locale, river, start, end, cb);
     }
-
-    /** Get the WQD data from service and stuck them up that tables. */
-    protected void fetchWQData() {
-        final Config config = Config.getInstance();
-        final String locale = config.getLocale();
-
-        final ArtifactDescription adescr = this.artifact.getArtifactDescription();
-        final DataList[] data = adescr.getOldData();
-
-        final double[] mm = getMinMaxKM(data);
-        final String river = getRiverName(data);
-
-        int i = 0;
-
-        // Get Data for respective gauge.
-        for (final Double[] range : this.gaugeRanges) {
-            // Gauge ranges overlap, move start and end a bit closer
-            // to each other.
-            final double rDiff = (range[1] - range[0]) / 10d;
-            final int fi = i;
-            this.wqInfoService.getWQInfo(locale, river, range[0] + rDiff, range[1] - rDiff, new AsyncCallback<WQInfoObject[]>() {
-                @Override
-                public void onFailure(final Throwable caught) {
-                    GWT.log("Could not recieve wq informations.");
-                    SC.warn(caught.getMessage());
-                }
-
-                @Override
-                public void onSuccess(final WQInfoObject[] wqi) {
-                    final int num = wqi != null ? wqi.length : 0;
-                    GWT.log("Received " + num + " wq informations (" + fi + ".");
-
-                    if (num == 0) {
-                        return;
-                    }
-
-                    addWQInfo(wqi, fi);
-                }
-            });
-            i++;
-        }
-    }
-
-    /** Add Info to helper table for gauge at index gaugeIdx. */
-    protected void addWQInfo(final WQInfoObject[] wqi, final int gaugeIdx) {
-        for (final WQInfoObject wi : wqi) {
-            final WQInfoRecord rec = new WQInfoRecord(wi);
-
-            if (wi.getType().equals("W")) {
-                this.wTables.get(gaugeIdx).addData(rec);
-            } else {
-                this.qdTables.get(gaugeIdx).addData(rec);
-            }
-        }
-    }
-
-    /**
-     * 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(final DataList[] data) {
-        final ArtifactDescription adesc = this.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(final DataList[] data) {
-        final ArtifactDescription adesc = this.artifact.getArtifactDescription();
-        return adesc.getRiver();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/bundu/BunduWstWQPanel.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/bundu/BunduWstWQPanel.java	Tue Jul 24 10:39:03 2018 +0200
@@ -9,17 +9,40 @@
 package org.dive4elements.river.client.client.ui.bundu;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.dive4elements.river.client.client.services.DynamicMainValuesService;
+import org.dive4elements.river.client.client.services.DynamicMainValuesServiceAsync;
+import org.dive4elements.river.client.client.ui.AbstractWQAdaptedInputPanel;
 import org.dive4elements.river.client.client.ui.DoubleArrayPanel;
-import org.dive4elements.river.client.client.ui.WQAdaptedInputPanel;
+import org.dive4elements.river.client.client.ui.wq.QDTable;
 import org.dive4elements.river.client.shared.model.Data;
 import org.dive4elements.river.client.shared.model.DataItem;
 import org.dive4elements.river.client.shared.model.DataList;
+import org.dive4elements.river.client.shared.model.DefaultData;
+import org.dive4elements.river.client.shared.model.DefaultDataItem;
+import org.dive4elements.river.client.shared.model.WQDataItem;
+import org.dive4elements.river.client.shared.model.WQInfoObject;
 
+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.types.TitleOrientation;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.types.Visibility;
 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.TextItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.Tab;
 
 /**
  * This UIProvider creates a widget to enter W or Q data for
@@ -27,33 +50,216 @@
  *
  * @author <a href="mailto:aheinecke@intevation.de">Andre Heinecke</a>
  */
-public class BunduWstWQPanel extends WQAdaptedInputPanel {
-    private static final long serialVersionUID = -3218827566805476423L;
+public class BunduWstWQPanel extends AbstractWQAdaptedInputPanel {
+
+    private static final long serialVersionUID = -1L;
+
+    private static enum mode {
+        Q, UD
+    }
+
+    /** Service to fetch W/Q MainValues. */
+    private final DynamicMainValuesServiceAsync mainValueService = GWT.create(DynamicMainValuesService.class);
+
+    private final RadioGroupItem radiogroup = new RadioGroupItem();
 
     private int bezugsjahr = 0;
+
     private int qSeriesLength = 0;
 
-    /** get bezugsjahr + range from server HERE **/
+    private Canvas list;
+
+    private final DynamicForm udForm = new DynamicForm();
+
+    private final TextItem udInputItem = new TextItem();
+
     @Override
-    public Canvas create(final DataList data) {
-
+    protected void beforeCreate(final DataList data) {
         for (final Data container : data.getAll()) {
-
-            for (final DataItem item : container.getItems()) {
-                if ("ignore_bezugsjahr".equals(item.getLabel())) {
-                    this.bezugsjahr = Integer.valueOf(item.getStringValue());
-                } else if ("ignore_q_series_length".equals(item.getLabel())) {
-                    this.qSeriesLength = Integer.valueOf(item.getStringValue());
+            final DataItem[] items = container.getItems();
+            if (items != null) {
+                for (final DataItem item : items) {
+                    if ("ignore_bezugsjahr".equals(item.getLabel())) {
+                        this.bezugsjahr = Integer.valueOf(item.getStringValue());
+                    } else if ("ignore_q_series_length".equals(item.getLabel())) {
+                        this.qSeriesLength = Integer.valueOf(item.getStringValue());
+                    }
                 }
             }
         }
-        return super.create(data);
     }
 
-    // TODO: ValidateRange verstehen und dann ggf. Löschen/Ändern/was auch immer
     @Override
-    protected List<String> validateRange(final Map<String, double[]> ranges) {
+    protected void afterCreate() {
+    }
+
+    @Override
+    protected Canvas createWidget(final DataList dataList) {
+        final VLayout layout = new VLayout();
+        final Canvas mode = createMode(dataList);
+
+        this.list = createList(dataList);
+
+        this.udInputItem.setShowTitle(false);
+        this.udInputItem.setWidth(120);
+        final IntegerRangeValidator validator = new IntegerRangeValidator();
+        validator.setMax(364);
+        validator.setMin(0);
+        validator.setErrorMessage(this.MSG.bundu_bezugswst_validation_range());
+        this.udInputItem.setRequiredMessage(this.MSG.bundu_bezugswst_validation_range());
+        this.udInputItem.setValidators(validator);
+        this.udInputItem.setValidateOnChange(true);
+        this.udForm.setFields(this.udInputItem);
+
+        final List<WQDataItem> items = getWQItems(dataList);
+        final int listHeight = ROW_HEIGHT * items.size();
+
+        mode.setHeight(25);
+        mode.setWidth(200);
+
+        layout.addMember(mode);
+        layout.addMember(this.list);
+        layout.addMember(this.udForm);
+        layout.setHeight(25 + listHeight);
+        layout.setWidth(350);
+        enableQ();
+        return layout;
+    }
+
+    /**
+     * Create radio button for switching
+     * Radiobutton-change also triggers helper panel tab selection.
+     */
+
+    private Canvas createMode(final DataList dataList) {
+        this.radiogroup.setShowTitle(false);
+        this.radiogroup.setVertical(false);
+        this.radiogroup.setWidth(200);
+
+        final LinkedHashMap<String, String> wqValues = new LinkedHashMap<String, String>();
+        wqValues.put(String.valueOf(mode.Q), this.MSG.wqQatGauge());
+        wqValues.put(String.valueOf(mode.UD), this.MSG.bundu_bezugswst_mode_ud());
+        this.radiogroup.setValueMap(wqValues);
+        this.radiogroup.setValue(String.valueOf(mode.Q));
+
+        this.modes = new DynamicForm();
+        this.modes.setFields(this.radiogroup);
+        this.modes.setWidth(200);
+        this.radiogroup.addChangeHandler(new ChangeHandler() {
+            @Override
+            public void onChange(final ChangeEvent e) {
+
+                final String value = e.getValue().toString();
+                if (value.equals(String.valueOf(mode.UD))) {
+                    enableQ();
+                } else if (value.equals(String.valueOf(mode.Q))) {
+                    enableUD();
+                }
+            }
+        });
+        return this.modes;
+    }
+
+    private void enableUD() {
+        BunduWstWQPanel.this.list.setVisibility(Visibility.VISIBLE);
+        BunduWstWQPanel.this.udForm.setVisibility(Visibility.HIDDEN);
+        BunduWstWQPanel.this.tabs.enable();
+    }
+
+    private void enableQ() {
+        BunduWstWQPanel.this.list.setVisibility(Visibility.HIDDEN);
+        BunduWstWQPanel.this.udForm.setVisibility(Visibility.VISIBLE);
+        BunduWstWQPanel.this.tabs.disable();
+    }
+
+    private final String getMode() {
+        return (String) this.radiogroup.getValue();
+    }
+
+    @Override
+    public Data[] getData() {
+        final Data[] data = new Data[2];
+        final String modeStr = getMode();
+        final DataItem item = new DefaultDataItem("bundu.wst.mode", "bundu.wst.mode", modeStr);
+        final Data modeData = new DefaultData("bundu.wst.mode", null, null, new DataItem[] { item });
+        data[0] = modeData;
+        if (modeStr.equals(mode.Q.toString())) {
+            data[1] = getWQValues("wq_values");
+        } else if (modeStr.equals(mode.UD.toString())) {
+            final String udValue = this.udInputItem.getValueAsString(); // in validate findet der int-check statt; er verhindert ein weitergehen, falls der Wert
+                                                                        // nicht int-kompatibel ist
+            final DataItem itemV = new DefaultDataItem("bundu.wst.ud_value", "bundu.wst.ud_value", udValue);
+            data[1] = new DefaultData("bundu.wst.ud_value", null, null, new DataItem[] { itemV });
+        }
+        return data;
+    }
+
+    @Override
+    protected void createTabs() {
+        for (int i = 0; i < this.gaugeRanges.size(); i++) {
+            final Tab qTab = new Tab(this.MSG.wq_table_q());
+            final QDTable qdTable = new QDTable();
+            this.qdTables.add(qdTable);
+            qdTable.showSelect();
+            qTab.setPane(qdTable);
+            this.tabs.addTab(qTab, i);
+        }
+        this.tabs.setSelectedTab(0);
+    }
+
+    private Canvas createList(final DataList dataList) {
+        final VLayout layout = new VLayout();
+
+        final List<WQDataItem> items = getWQItems(dataList);
+
+        int i = 0;
+        for (final DataItem item : items) {
+            if (item instanceof WQDataItem) {
+                final String title = item.getLabel();
+                final String label = item.getStringValue();
+
+                this.tabs.getTab(i).setTitle(this.tabs.getTab(i).getTitle() + " (" + label + ")");
+
+                final DoubleArrayPanel dap = new DoubleArrayPanel(label, null, this, null, TitleOrientation.LEFT);
+                this.wqranges.put(title, dap);
+                this.doubleArrayPanels.add(dap);
+                if (item instanceof WQDataItem) {
+                    final WQDataItem wq = (WQDataItem) item;
+                    final double[] mmQ = wq.getQRange();
+                    this.qranges.put(title, mmQ);
+
+                }
+                layout.addMember(dap);
+                i++;
+            }
+        }
+
+        layout.setHeight(items.size() * ROW_HEIGHT);
+
+        return layout;
+    }
+
+    @Override
+    public List<String> validate() {
         final List<String> errors = new ArrayList<String>();
+        if (getMode().equals(mode.Q.toString())) {
+            errors.addAll(this.validateRange(this.qranges));
+        } else if (getMode().equals(mode.UD.toString())) {
+            try {
+                if (this.udInputItem.validate()) {
+                    errors.add(this.udInputItem.getRequiredMessage());
+                }
+            }
+            catch (final NumberFormatException e) {
+                errors.add(this.MSG.error_invalid_integer());
+            }
+        }
+        return errors;
+    }
+
+    private List<String> validateRange(final Map<String, double[]> ranges) {
+        final List<String> errors = new ArrayList<String>();
+
         final NumberFormat nf = NumberFormat.getDecimalFormat();
 
         for (final DoubleArrayPanel dap : this.wqranges.values()) {
@@ -90,5 +296,53 @@
         }
         return errors;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+
+    @Override
+    public Canvas createOld(final DataList dataList) {
+        final List<Data> all = dataList.getAll();
+        final Data wqData = getData(all, "wq_values");
+        final Data wqMode = getData(all, "bundu.wst.mode");
+        final Data udValue = getData(all, "bundu.wst.ud_value");
+
+        final boolean isQ = wqMode.getItems()[0].getStringValue().equals(String.valueOf(mode.Q));
+        final Canvas back = getBackButton(dataList.getState());
+
+        final HLayout valLayout = new HLayout();
+        final HLayout modeLayout = new HLayout();
+        final VLayout vlayout = new VLayout();
+
+        final Label wqLabel = new Label(dataList.getLabel());
+        final Label modeLabel = new Label("");
+
+        wqLabel.setValign(VerticalAlignment.TOP);
+
+        wqLabel.setWidth(200);
+        wqLabel.setHeight(25);
+        modeLabel.setHeight(25);
+        modeLabel.setWidth(200);
+
+        valLayout.addMember(wqLabel);
+        if (isQ) {
+            valLayout.addMember(createOldWQValues(wqData, true));
+        } else {
+            valLayout.addMember(new Label(udValue.getItems()[0].getStringValue() + " [d/a]"));
+        }
+        valLayout.addMember(back);
+        modeLayout.addMember(modeLabel);
+
+        vlayout.addMember(valLayout);
+        vlayout.addMember(modeLayout);
+
+        return vlayout;
+    }
+
+    @Override
+    protected void callMainValuesService(final String locale, final String river, final double start, final double end,
+            final AsyncCallback<WQInfoObject[]> cb) {
+
+        final int startYear = this.bezugsjahr - this.qSeriesLength;
+        final int endYear = this.bezugsjahr;
+
+        this.mainValueService.getWQInfo(locale, river, start, end, startYear, endYear, cb);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/AbstractMainValuesServiceImpl.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,221 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.server;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.httpclient.exceptions.ConnectionException;
+import org.dive4elements.artifacts.httpclient.http.HttpClient;
+import org.dive4elements.artifacts.httpclient.http.HttpClientImpl;
+import org.dive4elements.river.client.shared.exceptions.ServerException;
+import org.dive4elements.river.client.shared.model.WQInfoObject;
+import org.dive4elements.river.client.shared.model.WQInfoObjectImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+abstract class AbstractMainValuesServiceImpl extends RemoteServiceServlet {
+
+    private static final Logger log = Logger.getLogger(DynamicMainValuesServiceImpl.class);
+
+    private static final Comparator<WQInfoObject> WQ_INFO_OBJECT_CMP = new Comparator<WQInfoObject>() {
+        @Override
+        public int compare(final WQInfoObject a, final WQInfoObject b) {
+
+            // Descending by type: Qs before Ds
+            final int cmp = a.getType().compareTo(b.getType());
+            if (cmp < 0)
+                return +1;
+            if (cmp > 0)
+                return -1;
+
+            // Ascending by value
+            final double diff = a.getValue() - b.getValue();
+            if (diff < 0d)
+                return -1;
+            if (diff > 0d)
+                return +1;
+            return 0;
+        }
+    };
+
+    private static final String ERROR_NO_WQINFO_FOUND = "error_no_wqinfo_found";
+
+    private static final String XPATH_WQS = "art:service/art:mainvalues/art:mainvalue";
+
+    private static final long serialVersionUID = 1L;
+
+    protected final Document createInput(final String river, final double from, final double to, final Integer startYear, final Integer endYear) {
+
+        final Document input = XMLUtils.newDocument();
+
+        final XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(input, ArtifactNamespaceContext.NAMESPACE_URI,
+                ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        final Element mainvalues = ec.create("mainvalues");
+
+        final Element riverEl = ec.create("river");
+        riverEl.setTextContent(river);
+        mainvalues.appendChild(riverEl);
+
+        final Element startEl = ec.create("start");
+        startEl.setTextContent(Double.valueOf(from).toString());
+        mainvalues.appendChild(startEl);
+
+        final Element endEl = ec.create("end");
+        endEl.setTextContent(Double.valueOf(to).toString());
+        mainvalues.appendChild(endEl);
+
+        if (startYear != null) {
+            final Element startYearElement = ec.create("startYear");
+            startYearElement.setTextContent(Integer.toString(startYear));
+            mainvalues.appendChild(startYearElement);
+        }
+
+        if (endYear != null) {
+            final Element endYearElement = ec.create("endYear");
+            endYearElement.setTextContent(Integer.toString(endYear));
+            mainvalues.appendChild(endYearElement);
+        }
+
+        input.appendChild(mainvalues);
+
+        return input;
+    }
+
+    protected final WQInfoObject[] callService(final String serviceName, final Document input, final String locale) throws ServerException {
+        final String url = getServletContext().getInitParameter("server-url");
+
+        final HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            final Document result = client.callService(url, serviceName, input);
+
+            log.debug("Extract wq info objects now.");
+            final WQInfoObject[] objects = extractWQInfoObjects(result);
+
+            if (objects.length > 0) {
+                return objects;
+            }
+        }
+        catch (final ConnectionException ce) {
+            log.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.
+     */
+    private WQInfoObject[] extractWQInfoObjects(final Document result) throws ServerException {
+        final NodeList list = (NodeList) XMLUtils.xpath(result, XPATH_WQS, XPathConstants.NODESET, ArtifactNamespaceContext.INSTANCE);
+
+        if (list == null || list.getLength() == 0) {
+            log.warn("No wq info found.");
+
+            throw new ServerException(ERROR_NO_WQINFO_FOUND);
+        }
+
+        final boolean debug = log.isDebugEnabled();
+
+        final int num = list.getLength();
+        if (debug) {
+            log.debug("Response contains " + num + " objects.");
+        }
+
+        final List<WQInfoObject> objects = new ArrayList<WQInfoObject>(num);
+
+        for (int i = 0; i < num; i++) {
+            final WQInfoObject obj = buildWQInfoObject(list.item(i));
+
+            if (obj != null) {
+                objects.add(obj);
+            }
+        }
+
+        if (debug) {
+            log.debug("Retrieved " + objects.size() + " wq values");
+        }
+
+        final WQInfoObject[] array = objects.toArray(new WQInfoObject[objects.size()]);
+
+        Arrays.sort(array, WQ_INFO_OBJECT_CMP);
+
+        return array;
+    }
+
+    /**
+     * 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.
+     */
+    private static WQInfoObject buildWQInfoObject(final Node node) {
+
+        final String name = XMLUtils.xpathString(node, "@name", ArtifactNamespaceContext.INSTANCE);
+
+        final String type = XMLUtils.xpathString(node, "@type", ArtifactNamespaceContext.INSTANCE);
+
+        final String value = XMLUtils.xpathString(node, "@value", ArtifactNamespaceContext.INSTANCE);
+
+        final String official = XMLUtils.xpathString(node, "@official", ArtifactNamespaceContext.INSTANCE);
+
+        final String starttime = XMLUtils.xpathString(node, "@starttime", ArtifactNamespaceContext.INSTANCE);
+
+        final String stoptime = XMLUtils.xpathString(node, "@stoptime", ArtifactNamespaceContext.INSTANCE);
+
+        if (name != null && type != null) {
+            try {
+                final Calendar cal = Calendar.getInstance();
+                java.util.Date start = null;
+                java.util.Date stop = null;
+                if (!starttime.equals("")) {
+                    cal.setTimeInMillis(Long.parseLong(starttime));
+                    start = cal.getTime();
+                }
+                if (!stoptime.equals("")) {
+                    cal.setTimeInMillis(Long.parseLong(stoptime));
+                    stop = cal.getTime();
+                }
+                return new WQInfoObjectImpl(name, type, new Double(value), official != null && official.equalsIgnoreCase("true"), start, stop);
+            }
+            catch (final NumberFormatException nfe) {
+                log.warn(nfe.getLocalizedMessage());
+            }
+        }
+
+        log.warn("Invalid wq info object found.");
+
+        return null;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/DynamicMainValuesServiceImpl.java	Tue Jul 24 10:39:03 2018 +0200
@@ -0,0 +1,30 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.server;
+
+import org.dive4elements.river.client.client.services.DynamicMainValuesService;
+import org.dive4elements.river.client.shared.exceptions.ServerException;
+import org.dive4elements.river.client.shared.model.WQInfoObject;
+import org.w3c.dom.Document;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DynamicMainValuesServiceImpl extends AbstractMainValuesServiceImpl implements DynamicMainValuesService {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public WQInfoObject[] getWQInfo(final String locale, final String river, final double from, final double to, final int startYear, final int endYear)
+            throws ServerException {
+
+        final Document input = createInput(river, from, to, startYear, endYear);
+
+        return callService("dynamicMainvalues", input, locale);
+    }
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/WQInfoServiceImpl.java	Tue Jul 24 10:36:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/WQInfoServiceImpl.java	Tue Jul 24 10:39:03 2018 +0200
@@ -8,233 +8,26 @@
 
 package org.dive4elements.river.client.server;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Comparator;
-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 org.dive4elements.artifacts.common.ArtifactNamespaceContext;
-import org.dive4elements.artifacts.common.utils.XMLUtils;
-
-import org.dive4elements.artifacts.httpclient.exceptions.ConnectionException;
-import org.dive4elements.artifacts.httpclient.http.HttpClient;
-import org.dive4elements.artifacts.httpclient.http.HttpClientImpl;
-
+import org.dive4elements.river.client.client.services.WQInfoService;
 import org.dive4elements.river.client.shared.exceptions.ServerException;
-import org.dive4elements.river.client.client.services.WQInfoService;
 import org.dive4elements.river.client.shared.model.WQInfoObject;
-import org.dive4elements.river.client.shared.model.WQInfoObjectImpl;
-
+import org.w3c.dom.Document;
 
 /**
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
-public class WQInfoServiceImpl
-extends      RemoteServiceServlet
-implements   WQInfoService
-{
-    private static final Logger log =
-        Logger.getLogger(WQInfoServiceImpl.class);
-
-    public static final String ERROR_NO_WQINFO_FOUND =
-        "error_no_wqinfo_found";
+public class WQInfoServiceImpl extends AbstractMainValuesServiceImpl implements WQInfoService {
+    private static final long serialVersionUID = 1L;
 
-    public static final String XPATH_WQS =
-        "art:service/art:mainvalues/art:mainvalue";
-
+    private static final Logger log = Logger.getLogger(WQInfoServiceImpl.class);
 
-    public WQInfoObject[] getWQInfo(
-        String locale,
-        String river,
-        double from,
-        double to)
-    throws ServerException
-    {
+    @Override
+    public WQInfoObject[] getWQInfo(final String locale, final String river, final double from, final double to) throws ServerException {
         log.info("WQInfoServiceImpl.getWQInfo");
 
-        String url = getServletContext().getInitParameter("server-url");
-
-        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);
-
-            log.debug("Extract wq info objects now.");
-            WQInfoObject[] objects = extractWQInfoObjects(result);
-
-            if (objects.length > 0) {
-                return objects;
-            }
-        }
-        catch (ConnectionException ce) {
-            log.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) {
-            log.warn("No wq info found.");
-
-            throw new ServerException(ERROR_NO_WQINFO_FOUND);
-        }
-
-        boolean debug = log.isDebugEnabled();
-
-        int num = list.getLength();
-        if (debug) {
-            log.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);
-            }
-        }
+        final Document input = createInput(river, from, to, null, null);
 
-        if (debug) {
-            log.debug("Retrieved " + objects.size() + " wq values");
-        }
-
-        WQInfoObject [] array = (WQInfoObject[])
-            objects.toArray(new WQInfoObject[objects.size()]);
-
-        Arrays.sort(array, WQ_INFO_OBJECT_CMP);
-
-        return array;
+        return callService("mainvalues", input, locale);
     }
-
-    public static final Comparator<WQInfoObject> WQ_INFO_OBJECT_CMP =
-        new Comparator<WQInfoObject>() {
-            @Override
-            public int compare(WQInfoObject a, WQInfoObject b) {
-
-                // Descending by type: Qs before Ds
-                int cmp = a.getType().compareTo(b.getType());
-                if (cmp < 0) return +1;
-                if (cmp > 0) return -1;
-
-                // Ascending by value
-                double diff = a.getValue() - b.getValue();
-                if (diff < 0d) return -1;
-                if (diff > 0d) return +1;
-                return 0;
-            }
-        };
-
-    /**
-     * 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 static 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);
-
-        String official = XMLUtils.xpathString(
-            node, "@official", ArtifactNamespaceContext.INSTANCE);
-
-        String starttime = XMLUtils.xpathString(
-            node, "@starttime", ArtifactNamespaceContext.INSTANCE);
-
-        String stoptime = XMLUtils.xpathString(
-            node, "@stoptime", ArtifactNamespaceContext.INSTANCE);
-
-        if (name != null && type != null) {
-            try {
-                Calendar cal = Calendar.getInstance();
-                java.util.Date start = null;
-                java.util.Date stop = null;
-                if (!starttime.equals("")) {
-                    cal.setTimeInMillis(Long.parseLong(starttime));
-                    start = cal.getTime();
-                }
-                if (!stoptime.equals("")) {
-                    cal.setTimeInMillis(Long.parseLong(stoptime));
-                    stop = cal.getTime();
-                }
-                return new WQInfoObjectImpl(
-                    name,
-                    type,
-                    new Double(value),
-                    official != null && official.equalsIgnoreCase("true"),
-                    start,
-                    stop);
-            }
-            catch (NumberFormatException nfe) {
-                log.warn(nfe.getLocalizedMessage());
-            }
-        }
-
-        log.warn("Invalid wq info object found.");
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file

http://dive4elements.wald.intevation.org