changeset 9227:84397da33d17

Allow to control specific behaviour in TwinDatacagePanel Implemented client logic of 'intelligent datacage filtering' for SINFO
author gernotbelger
date Wed, 04 Jul 2018 18:28:08 +0200
parents 83aee0942eae
children dba14da43f23
files 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/ui/AbstractPairRecommendationPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacagePairWidget.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageTwinPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WaterlevelTwinPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/FlowDepthMinMaxTwinPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/FlowDepthTwinPanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/WaterlevelSoundingSelectionController.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/WaterlevelSoundingYearFilter.java gwt-client/src/main/java/org/dive4elements/river/client/shared/model/AttrList.java gwt-client/src/main/java/org/dive4elements/river/client/shared/model/ToLoad.java gwt-client/src/test/java/test/SimpleRecommendation.java
diffstat 15 files changed, 554 insertions(+), 271 deletions(-) [+]
line wrap: on
line diff
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Wed Jul 04 18:28:08 2018 +0200
@@ -143,6 +143,8 @@
 
     String databasket_loading();
 
+    String databasket_empty();
+
     String theme_top();
 
     String theme_up();
@@ -1550,4 +1552,8 @@
     String sinfo_columnlabel_waterlevels();
 
     String error_limit_exceeded_salix();
+
+    String sinfo_sounding_waterlevel_select_waterlevel();
+
+    String sinfo_deactivate_intelligent_datacord();
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Wed Jul 04 18:28:08 2018 +0200
@@ -57,6 +57,7 @@
 scale = Scale
 databasket = Databasket
 databasket_loading = Loading Databasket content
+databasket_empty = Databasket is empty
 fix = Fixing
 fixanalysis = Fixing Analysis
 next = Next  
@@ -835,4 +836,6 @@
 export_csv_title = Title: 
 
 sinfo_columnlabel_soundings = Wahl der Peilung
-sinfo_columnlabel_waterlevels = Wahl des Wasserspiegels
\ No newline at end of file
+sinfo_columnlabel_waterlevels = Wahl des Wasserspiegels
+sinfo_sounding_waterlevel_select_waterlevel = W\u00e4hlen Sie zuerst eine Peilung
+sinfo_deactivate_intelligent_datacord = Intelligente Datenkorbfilterung ausschalten
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Wed Jul 04 18:28:08 2018 +0200
@@ -57,6 +57,7 @@
 scale = Skalierung
 databasket = Datenkorb
 databasket_loading=Lade Datenkorb Inhalt
+databasket_empty = Datenkorb Inhalt ist leer
 fix = Fixierungsanalyse
 fixanalysis = Fixierungsanalyse
 next = Weiter 
@@ -835,4 +836,6 @@
 export_csv_title = Titel: 
 
 sinfo_columnlabel_soundings = Wahl der Peilung
-sinfo_columnlabel_waterlevels = Wahl des Wasserspiegels
\ No newline at end of file
+sinfo_columnlabel_waterlevels = Wahl des Wasserspiegels
+sinfo_sounding_waterlevel_select_waterlevel = W\u00e4hlen Sie zuerst eine Peilung
+sinfo_deactivate_intelligent_datacord = Intelligente Datenkorbfilterung ausschalten
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractPairRecommendationPanel.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractPairRecommendationPanel.java	Wed Jul 04 18:28:08 2018 +0200
@@ -52,39 +52,36 @@
 public abstract class AbstractPairRecommendationPanel
 extends      TextProvider {
 
-	/**
-	 * Allows for abstraction on how to handle/serialize the recommendation and the used factories.
-	 * Basically this allows to tweak the factory that is delivered from the datacage to be replaced by a specific one...
-	 * 
-	 * @author Gernot Belger
-	 */
-	public static interface IRecommendationInfo	{
-
-		String getFactory(String originalFactory);
+    /**
+     * Allows for abstraction on how to handle/serialize the recommendation and the used factories.
+     * Basically this allows to tweak the factory that is delivered from the datacage to be replaced by a specific one...
+     *
+     * @author Gernot Belger
+     */
+    public static interface IRecommendationInfo	{
 
-		/**
-		 * Separate factory for the 'createDataString' method, because in the case of waterlevels. See HOTFIX/FIXME there.
-		 * @param recommendation 
-		 */
-		String getDataStringFactory(Recommendation recommendation);
+        String getFactory(String originalFactory);
 
-	    /**
-	     * Set factory of recommendation such that the correct artifacts will
-	     * be cloned for difference calculations.
-	     */
-	    void adjustRecommendation(Recommendation recommendation);
-	}
-	
-	public static interface IValidator
-	{
-		List<String> validate(ListGrid differencesList, FLYSConstants msgProvider);
-	}
+        /**
+         * Separate factory for the 'createDataString' method, because in the case of waterlevels. See HOTFIX/FIXME there.
+         * @param recommendation
+         */
+        String getDataStringFactory(Recommendation recommendation);
+
+        /**
+         * Set factory of recommendation such that the correct artifacts will
+         * be cloned for difference calculations.
+         */
+        void adjustRecommendation(Recommendation recommendation);
+    }
+
+    public static interface IValidator
+    {
+        List<String> validate(ListGrid differencesList, FLYSConstants msgProvider);
+    }
 
     private static final long serialVersionUID = 8906629596491827857L;
 
-    // FIXME: why? we hide the field of the super class with exactly the same thing...
-    private static FLYSConstants MSG_PROVIDER = GWT.create(FLYSConstants.class);
-
     private String dataName;
 
     private final User user;
@@ -97,7 +94,7 @@
      * be able to identify artifacts that can be removed from the collection.
      */
     private final List<RecommendationPairRecord> removedPairs =
-        new ArrayList<RecommendationPairRecord>();
+            new ArrayList<RecommendationPairRecord>();
 
     /** Service handle to clone and add artifacts to collection. */
     private final LoadArtifactServiceAsync loadArtifactService = GWT.create(
@@ -107,28 +104,28 @@
     private final RemoveArtifactServiceAsync removeArtifactService = GWT.create(
             org.dive4elements.river.client.client.services.RemoveArtifactService.class);
 
-	private final IRecommendationInfo leftInfo;
-
-	private final IRecommendationInfo rightInfo;
+    private final IRecommendationInfo leftInfo;
 
-	private final IValidator validator;
+    private final IRecommendationInfo rightInfo;
 
-	/**
-	 * @param Validates the content of this form when the user clicks 'apply'
-	 * @param leftInfo Delegate for handling the left part of the recommendation-pair
-	 * @param rightInfo Delegate for handling the right part of the recommendation-pair
-	 */
+    private final IValidator validator;
+
+    /**
+     * @param Validates the content of this form when the user clicks 'apply'
+     * @param leftInfo Delegate for handling the left part of the recommendation-pair
+     * @param rightInfo Delegate for handling the right part of the recommendation-pair
+     */
     public AbstractPairRecommendationPanel(final User user, final IValidator validator, final IRecommendationInfo leftInfo, final IRecommendationInfo rightInfo ) {
         this.user = user;
-		this.validator = validator;
-		this.leftInfo = leftInfo;
-		this.rightInfo = rightInfo;
+        this.validator = validator;
+        this.leftInfo = leftInfo;
+        this.rightInfo = rightInfo;
     }
-    
+
     // FIXME: better than copy/pasting the MSG field into every sub-class but not really nice yet.
     protected final static FLYSConstants msg() {
-		return MSG_PROVIDER;
-	}
+        return MSG;
+    }
 
     /**
      * Remove first occurrence of "[" and "]" (if both do occur).
@@ -155,7 +152,7 @@
     /**
      * Create a recommendation from a string representation of it.
      * @param from string in format as shown above.
-     * @param leftInfo2 
+     * @param leftInfo2
      * @return recommendation from input string.
      */
     private Recommendation createRecommendationFromString(final String from, final IRecommendationInfo info) {
@@ -169,7 +166,7 @@
         final List<Recommendation.Facet> facets = new ArrayList<Recommendation.Facet>();
         facets.add(facet);
         filter.add("longitudinal_section", facets);
-        
+
         final String factory = info.getFactory( parts[1] );
 
         final  Recommendation r = new Recommendation(factory, parts[0], this.artifact.getUuid(), filter);
@@ -188,12 +185,12 @@
         if (recs.length % 2 != 0) return;
         for (int i = 0; i < recs.length; i+=2) {
             final Recommendation minuend =
-                createRecommendationFromString(recs[i+0], this.leftInfo);
+                    createRecommendationFromString(recs[i+0], this.leftInfo);
             final Recommendation subtrahend =
-                createRecommendationFromString(recs[i+1], this.rightInfo);
+                    createRecommendationFromString(recs[i+1], this.rightInfo);
 
             final RecommendationPairRecord pr = new RecommendationPairRecord(
-                minuend, subtrahend);
+                    minuend, subtrahend);
             // This Recommendation Pair comes from the data string and was thus
             // already cloned.
             pr.setIsAlreadyLoaded(true);
@@ -208,13 +205,13 @@
      */
     @Override
     public final Canvas create(final DataList dataList) {
-    	
+
         final Canvas widget = createWidget();
-    	
-    	final Canvas canvas = createChooserWidgets(widget, dataList, this.user, this.differencesList);
-    	
+
+        final Canvas canvas = createChooserWidgets(widget, dataList, this.user, this.differencesList);
+
         populateGrid(dataList);
-        
+
         return canvas;
     }
 
@@ -223,14 +220,14 @@
      */
     protected abstract Canvas createChooserWidgets(final Canvas widget, final DataList dataList, final User auser, final ListGrid diffList);
 
-	private void populateGrid(final DataList dataList) {
+    private void populateGrid(final DataList dataList) {
         final Data data     = dataList.get(0);
         this.dataName = data.getLabel();
         for (int i = 0; i < dataList.size(); i++) {
             if (dataList.get(i) != null && dataList.get(i).getItems() != null) {
                 if (dataList.get(i).getItems() != null) {
                     populateGridFromString(
-                        dataList.get(i).getItems()[0].getStringValue());
+                            dataList.get(i).getItems()[0].getStringValue());
                 }
             }
         }
@@ -238,7 +235,7 @@
 
     @Override
     public final List<String> validate() {
-    	return this.validator.validate(this.differencesList, MSG_PROVIDER);
+        return this.validator.validate(this.differencesList, msg());
     }
 
     /**
@@ -259,34 +256,34 @@
         // Track removed rows, therefore more or less reimplement
         // setCanRecomeRecords.
         final ListGridField removeField  =
-            new ListGridField("_removeRecord", "Remove Record"){{
-                setType(ListGridFieldType.ICON);
-                setIcon(GWT.getHostPageBaseURL() + msg().removeFeature());
-                setCanEdit(false);
-                setCanFilter(false);
-                setCanSort(false);
-                setCanGroupBy(false);
-                setCanFreeze(false);
-                setWidth(25);
-        }};
-
-        this.differencesList.setFields(new ListGridField[] {nameField,
-           capitalField, removeField});
+                new ListGridField("_removeRecord", "Remove Record"){{
+                    setType(ListGridFieldType.ICON);
+                    setIcon(GWT.getHostPageBaseURL() + msg().removeFeature());
+                    setCanEdit(false);
+                    setCanFilter(false);
+                    setCanSort(false);
+                    setCanGroupBy(false);
+                    setCanFreeze(false);
+                    setWidth(25);
+                }};
 
-        this.differencesList.addRecordClickHandler(new RecordClickHandler() {
-                @Override
-                public void onRecordClick(final RecordClickEvent event) {
-                    // Just handle remove-clicks
-                    if(!event.getField().getName().equals(removeField.getName())) {
-                        return;
+                this.differencesList.setFields(new ListGridField[] {nameField,
+                        capitalField, removeField});
+
+                this.differencesList.addRecordClickHandler(new RecordClickHandler() {
+                    @Override
+                    public void onRecordClick(final RecordClickEvent event) {
+                        // Just handle remove-clicks
+                        if(!event.getField().getName().equals(removeField.getName())) {
+                            return;
+                        }
+                        trackRemoved(event.getRecord());
+                        event.getViewer().removeData(event.getRecord());
                     }
-                    trackRemoved(event.getRecord());
-                    event.getViewer().removeData(event.getRecord());
-                }
-            });
-        layout.addMember(this.differencesList);
+                });
+                layout.addMember(this.differencesList);
 
-        return layout;
+                return layout;
     }
 
 
@@ -322,7 +319,7 @@
 
         for (final ListGridRecord record : records) {
             final RecommendationPairRecord r =
-                (RecommendationPairRecord) record;
+                    (RecommendationPairRecord) record;
             // Do not add "old" recommendations.
             if (!r.isAlreadyLoaded()) {
                 // Check whether one of those is a dike or similar.
@@ -374,47 +371,47 @@
         // feedback.
         for(final String uuid: artifactIdsToRemove) {
             this.removeArtifactService.remove(this.collection,
-                uuid,
-                locale,
-                new AsyncCallback<Collection>() {
-                    @Override
-                    public void onFailure(final Throwable caught) {
-                        GWT.log("RemoveArtifact (" + uuid + ") failed.");
-                    }
-                    @Override
-                    public void onSuccess(final Collection coll) {
-                        GWT.log("RemoveArtifact succeeded");
-                    }
-                });
+                    uuid,
+                    locale,
+                    new AsyncCallback<Collection>() {
+                @Override
+                public void onFailure(final Throwable caught) {
+                    GWT.log("RemoveArtifact (" + uuid + ") failed.");
+                }
+                @Override
+                public void onSuccess(final Collection coll) {
+                    GWT.log("RemoveArtifact succeeded");
+                }
+            });
         }
 
         // Clone new ones (and spawn statics), go forward.
         this.parameterList.lockUI();
         this.loadArtifactService.loadMany(
-            this.collection,
-            toClone,
-            //"staticwkms" and "waterlevel"
-            null,
-            locale,
-            new AsyncCallback<Artifact[]>() {
-                @Override
-                public void onFailure(final Throwable caught) {
-                	caught.printStackTrace();
-                    GWT.log("Failure of cloning with factories!");
-                    AbstractPairRecommendationPanel.this.parameterList.unlockUI();
-                }
-                @Override
-                public void onSuccess(final Artifact[] artifacts) {
-                    GWT.log("Successfully cloned " + toClone.length +
-                        " with factories.");
+                this.collection,
+                toClone,
+                //"staticwkms" and "waterlevel"
+                null,
+                locale,
+                new AsyncCallback<Artifact[]>() {
+                    @Override
+                    public void onFailure(final Throwable caught) {
+                        caught.printStackTrace();
+                        GWT.log("Failure of cloning with factories!");
+                        AbstractPairRecommendationPanel.this.parameterList.unlockUI();
+                    }
+                    @Override
+                    public void onSuccess(final Artifact[] artifacts) {
+                        GWT.log("Successfully cloned " + toClone.length +
+                                " with factories.");
 
-                    fireStepForwardEvent(new StepForwardEvent(
-                        getData(toClone, artifacts, toUse)));
-                    AbstractPairRecommendationPanel.this.parameterList.unlockUI();
-                }
-            });
+                        fireStepForwardEvent(new StepForwardEvent(
+                                getData(toClone, artifacts, toUse)));
+                        AbstractPairRecommendationPanel.this.parameterList.unlockUI();
+                    }
+                });
     }
-    
+
     /**
      * Create Data and DataItem from selection (a long string with identifiers
      * to construct diff-pairs).
@@ -440,21 +437,21 @@
             final String uuid = newArtifact.getUuid();
             r.setMasterArtifact(uuid);
 
-            if (i>0) 
-            	dataItemString += "#";
+            if (i>0)
+                dataItemString += "#";
 
             // REMARK: ugly, but we know that the recommandations comes in left/right pairs.
             final IRecommendationInfo info = i % 2 == 0 ? this.leftInfo : this.rightInfo;
-            
+
             dataItemString += createDataString(uuid, r, info);
         }
 
         for (int i = 0; i < oldRecommendations.length; i++) {
             final Recommendation r = oldRecommendations[i];
             final String uuid = r.getIDs();
-            
-            if (dataItemString.length() > 0) 
-            	dataItemString += "#";
+
+            if (dataItemString.length() > 0)
+                dataItemString += "#";
 
             // REMARK: ugly, but we know that the recommandations comes in left/right pairs.
             final IRecommendationInfo info = i % 2 == 0 ? this.leftInfo : this.rightInfo;
@@ -466,7 +463,7 @@
         // (e.g. one per pair).
         final DataItem item = new DefaultDataItem(this.dataName, this.dataName, dataItemString);
         return new Data[] { new DefaultData(
-            this.dataName, null, null, new DataItem[] {item}) };
+                this.dataName, null, null, new DataItem[] {item}) };
     }
 
     /**
@@ -475,35 +472,35 @@
      * @param info Provides the factory to encode.
      */
     protected static final String createDataString(final String artifactUuid, final Recommendation recommendation, final IRecommendationInfo info) {
-    	final String factory = info.getDataStringFactory( recommendation );
-        
-    	final Filter filter = recommendation.getFilter();
-		Facet  f      = null;
-		
-		if(filter != null) {
-		    final Map<String, List<Facet>>               outs = filter.getOuts();
-		    final Set<Map.Entry<String, List<Facet>>> entries = outs.entrySet();
-		
-		    for (final Map.Entry<String, List<Facet>> entry: entries) {
-		        final List<Facet> fs = entry.getValue();
-		
-		        f = fs.get(0);
-		        if (f != null) {
-		            break;
-		        }
-		    }
-		
-		    return "[" + artifactUuid + ";"
-		        + f.getName()
-		        + ";"
-		        + f.getIndex()
-		        + ";"
-		        + recommendation.getDisplayName() + "]";
-		}
-		
-		return "["
-		    + artifactUuid
-		    + ";" + factory + ";0;"
-		    + recommendation.getDisplayName() + "]";
+        final String factory = info.getDataStringFactory( recommendation );
+
+        final Filter filter = recommendation.getFilter();
+        Facet  f      = null;
+
+        if(filter != null) {
+            final Map<String, List<Facet>>               outs = filter.getOuts();
+            final Set<Map.Entry<String, List<Facet>>> entries = outs.entrySet();
+
+            for (final Map.Entry<String, List<Facet>> entry: entries) {
+                final List<Facet> fs = entry.getValue();
+
+                f = fs.get(0);
+                if (f != null) {
+                    break;
+                }
+            }
+
+            return "[" + artifactUuid + ";"
+            + f.getName()
+            + ";"
+            + f.getIndex()
+            + ";"
+            + recommendation.getDisplayName() + "]";
+        }
+
+        return "["
+        + artifactUuid
+        + ";" + factory + ";0;"
+        + recommendation.getDisplayName() + "]";
     }
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacagePairWidget.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacagePairWidget.java	Wed Jul 04 18:28:08 2018 +0200
@@ -25,8 +25,26 @@
  * Insert a record into a listgrid when add-this-button clicked.
  */
 public class DatacagePairWidget extends VLayout {
+
+    /**
+     * Allows for advanced controlling of the behavior of this pair widget.
+     */
+    public static interface IDatacagePairControler {
+        void setup(DatacageWidget leftWidget, DatacageWidget rightWidget, HLayout toolbarLayout);
+    }
+
+    /**
+     * {@link IDatacagePairControler} implementation that does nothing.
+     */
+    public static final IDatacagePairControler NIL_CONTROLER = new IDatacagePairControler() {
+        @Override
+        public void setup(final DatacageWidget leftWidget, final DatacageWidget rightWidget, final HLayout toolbarLayout) {
+            // does nothing
+        }
+    };
+
     /** i18n resource. */
-    private final FLYSConstants MSG = GWT.create(FLYSConstants.class);
+    private static final FLYSConstants MSG = GWT.create(FLYSConstants.class);
 
     /** The "remote" ListGrid to insert data to when add-button is clicked. */
     private final ListGrid grid;
@@ -43,8 +61,9 @@
     /** errorMsg maxCount */
     private final String msgMaxCount;
 
-    public DatacagePairWidget(final DatacageWidgetData leftData, final DatacageWidgetData rightData, final ListGrid grid, final int maxCount,
-            final String msgMaxCount) {
+    public DatacagePairWidget(final IDatacagePairControler controler, final DatacageWidgetData leftData, final DatacageWidgetData rightData,
+            final ListGrid grid, final int maxCount, final String msgMaxCount) {
+
         this.msgMaxCount = msgMaxCount;
         this.maxCount = maxCount;
         this.grid = grid;
@@ -60,7 +79,10 @@
         hLayout.addMember(this.firstDatacageWidget);
         hLayout.addMember(this.secondDatacageWidget);
 
-        final Button plusBtn = new Button(this.MSG.datacage_add_pair());
+        final HLayout toolbarLayout = new HLayout();
+        toolbarLayout.setAutoHeight();
+
+        final Button plusBtn = new Button(MSG.datacage_add_pair());
         plusBtn.setAutoFit(true);
         plusBtn.addClickHandler(new ClickHandler() {
             @Override
@@ -68,23 +90,26 @@
                 plusClicked();
             }
         });
+        toolbarLayout.addMember(plusBtn);
 
         addMember(hLayout);
-        addMember(plusBtn);
+        addMember(toolbarLayout);
+
+        controler.setup(this.firstDatacageWidget, this.secondDatacageWidget, toolbarLayout);
     }
 
     /**
      * Callback for add-button.
      * Fires to load for every selected element and handler.
      */
-    public void plusClicked() {
+    protected final void plusClicked() {
         final ToLoad toLoad1 = this.firstDatacageWidget.getSelection();
         final ToLoad toLoad2 = this.secondDatacageWidget.getSelection();
 
         // FIXME: allows to select folders... but it should not
 
         if (toLoad1 == null || toLoad2 == null || toLoad1.toRecommendations().isEmpty() || toLoad2.toRecommendations().isEmpty()) {
-            SC.say(this.MSG.warning_select_two_values());
+            SC.say(MSG.warning_select_two_values());
             return;
         }
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageTwinPanel.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageTwinPanel.java	Wed Jul 04 18:28:08 2018 +0200
@@ -8,6 +8,7 @@
 
 package org.dive4elements.river.client.client.ui;
 
+import org.dive4elements.river.client.client.ui.DatacagePairWidget.IDatacagePairControler;
 import org.dive4elements.river.client.shared.model.DataList;
 import org.dive4elements.river.client.shared.model.User;
 
@@ -24,22 +25,28 @@
  */
 public abstract class DatacageTwinPanel extends AbstractPairRecommendationPanel {
 
+    private static final long serialVersionUID = 1L;
+
     private final IDatacageTwinPanelInfo leftInfo;
     private final IDatacageTwinPanelInfo rightInfo;
 
     private final int maxCount;
     private final String msgMaxCount;
 
+    private final IDatacagePairControler controler;
+
     public static interface IDatacageTwinPanelInfo extends IRecommendationInfo {
         String getOuts();
 
         String getColumnLabel();
     }
 
-    public DatacageTwinPanel(final User user, final IValidator validator, final IDatacageTwinPanelInfo leftInfo, final IDatacageTwinPanelInfo rightInfo,
+    public DatacageTwinPanel(final User user, final IValidator validator, final IDatacagePairControler controler, final IDatacageTwinPanelInfo leftInfo,
+            final IDatacageTwinPanelInfo rightInfo,
             final int maxCount, final String msgMaxCount) {
         super(user, validator, leftInfo, rightInfo);
 
+        this.controler = controler;
         this.maxCount = maxCount;
         this.msgMaxCount = msgMaxCount;
         this.leftInfo = leftInfo;
@@ -63,7 +70,7 @@
         final String rightLabel = this.rightInfo.getColumnLabel();
         final DatacageWidgetData rightData = new DatacageWidgetData(this.artifact, user, rightOuts, "load-system:true", false, rightLabel);
 
-        helperLayout.addMember(new DatacagePairWidget(leftData, rightData, differencesList, this.maxCount, this.msgMaxCount));
+        helperLayout.addMember(new DatacagePairWidget(this.controler, leftData, rightData, differencesList, this.maxCount, this.msgMaxCount));
 
         if (dataList != null && dataList.get(0) != null && dataList.get(0).getDescription() != null) {
             final Label title = new Label(dataList.get(0).getDescription());
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java	Wed Jul 04 18:28:08 2018 +0200
@@ -48,7 +48,10 @@
  * Display tree of, for example, previous calculations and allows
  * selection in order to access/clone these.
  */
-public class DatacageWidget extends VLayout {
+public final class DatacageWidget extends VLayout {
+
+    private static final FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
     public static final int MAX_OPEN = 30;
 
     public interface DatacageFilter {
@@ -62,10 +65,15 @@
         }
     };
 
+    public static final DatacageFilter ACCEPT_NONE_FILTER = new DatacageFilter() {
+        @Override
+        public boolean accept(final DataCageNode node) {
+            return false;
+        }
+    };
+
     private final MetaDataServiceAsync metaDataService = GWT.create(MetaDataService.class);
 
-    private final FLYSConstants messages = GWT.create(FLYSConstants.class);
-
     private final List<DatacageHandler> handlers = new ArrayList<DatacageHandler>();
 
     private final List<DatacageDoubleClickHandler> doubleHandlers = new ArrayList<DatacageDoubleClickHandler>();
@@ -105,11 +113,9 @@
         this.treeGrid.setShowRoot(false);
         this.treeGrid.setNodeIcon("[SKIN]/../blank.gif");
         this.treeGrid.setShowConnectors(true);
-        this.treeGrid.setLoadingMessage(this.messages.databasket_loading());
-        this.treeGrid.setLoadingDataMessage(this.messages.databasket_loading());
-
-        // FIXME: other message
-        this.treeGrid.setEmptyMessage(this.messages.databasket_loading());
+        this.treeGrid.setLoadingMessage(MSG.databasket_loading());
+        this.treeGrid.setLoadingDataMessage(MSG.databasket_loading());
+        this.treeGrid.setEmptyMessage(MSG.databasket_empty());
 
         this.treeGrid.setHoverMoveWithMouse(true);
         this.treeGrid.setCanHover(true);
@@ -157,6 +163,10 @@
             updateTree(this.dcTree);
     }
 
+    public TreeGrid getTreeGrid() {
+        return this.treeGrid;
+    }
+
     /** Disable input, show spinning wheel of joy. */
     private void lockUI() {
         this.lockScreen = ScreenLock.lockUI(this, this.lockScreen);
@@ -211,11 +221,13 @@
 
     public ToLoad getSelection() {
         // Reset content of toLoads.
+
+        // FIXME: bad... instead we should react to selection events and update toLoad in this way.
+
         this.toLoad = new ToLoad();
 
-        if (this.treeGrid == null) {
+        if (this.treeGrid == null)
             return this.toLoad;
-        }
 
         final ListGridRecord[] selection = this.treeGrid.getSelectedRecords();
 
@@ -278,7 +290,7 @@
     }
 
     private Button createPlusButton() {
-        final Button plusBtn = new Button(this.messages.datacageAdd());
+        final Button plusBtn = new Button(MSG.datacageAdd());
         plusBtn.addClickHandler(new ClickHandler() {
             @Override
             public void onClick(final ClickEvent event) {
@@ -312,13 +324,13 @@
      * Afterwards, add all children of node to stack to parse (next time
      * collectToLoads is called).
      */
-    private void collectToLoads(TreeNode node) {
+    private void collectToLoads(final TreeNode root1) {
         final Stack<TreeNode> stack = new Stack<TreeNode>();
 
-        stack.push(node);
+        stack.push(root1);
 
         while (!stack.isEmpty()) {
-            node = stack.pop();
+            final TreeNode node = stack.pop();
             final String factory = node.getAttribute("factory");
             if (factory != null) { // we need at least a factory
                 final String artifact = node.getAttribute("artifact-id");
@@ -384,7 +396,7 @@
         updateTree(this.dcTree);
     }
 
-    private void updateTree(final DataCageTree data) {
+    private void updateTree(final DataCageTree dcData) {
 
         this.tree = new Tree();
         this.tree.setModelType(TreeModelType.CHILDREN);
@@ -394,7 +406,7 @@
         this.tree.setShowRoot(false);
 
         final IdGenerator idGenerator = new IdGenerator();
-        final DataCageNode dcRoot = data.getRoot();
+        final DataCageNode dcRoot = dcData.getRoot();
         final TreeNode root = buildRecursiveChildren(dcRoot, idGenerator);
         if (root != null) {
             this.tree.setRoot(root);
@@ -428,19 +440,18 @@
         }
     } // class IdGenerator
 
-    private String i18n(String s) {
-        if (!(s.startsWith("${") && s.endsWith("}"))) {
+    private String i18n(final String s) {
+        if (!(s.startsWith("${") && s.endsWith("}")))
             return s;
-        }
 
-        s = s.substring(2, s.length() - 1);
+        final String sub = s.substring(2, s.length() - 1);
 
         try {
-            return this.messages.getString(s);
+            return MSG.getString(sub);
         }
         catch (final MissingResourceException mre) {
-            GWT.log("cannot find i18n for + '" + s + "'");
-            return s;
+            GWT.log("cannot find i18n for + '" + sub + "'", mre);
+            return sub;
         }
     }
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WaterlevelTwinPanel.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WaterlevelTwinPanel.java	Wed Jul 04 18:28:08 2018 +0200
@@ -18,8 +18,10 @@
  */
 public class WaterlevelTwinPanel extends DatacageTwinPanel {
 
+    private static final long serialVersionUID = 1L;
+
     public WaterlevelTwinPanel(final User user) {
-        super(user, new WaterlevelTwinPanelValidator(), new WaterlevelRecommendationInfo("winfo_diff_twin_panel"),
+        super(user, new WaterlevelTwinPanelValidator(), DatacagePairWidget.NIL_CONTROLER, new WaterlevelRecommendationInfo("winfo_diff_twin_panel"),
                 new WaterlevelRecommendationInfo("winfo_diff_twin_panel"), 9999, "");
     }
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/FlowDepthMinMaxTwinPanel.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/FlowDepthMinMaxTwinPanel.java	Wed Jul 04 18:28:08 2018 +0200
@@ -27,6 +27,6 @@
     private static final NilDatacageTwinPanelInfo RIGHT_INFO = new NilDatacageTwinPanelInfo(MSG.sinfo_columnlabel_waterlevels(), "sinfo_flowdepth_waterlevels");
 
     public FlowDepthMinMaxTwinPanel(final User user) {
-        super(user, new FlowDepthTwinPanelValidator(), LEFT_INFO, RIGHT_INFO, 9999, "");
+        super(user, new FlowDepthTwinPanelValidator(), new WaterlevelSoundingSelectionController(MSG), LEFT_INFO, RIGHT_INFO, 9999, "");
     }
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/FlowDepthTwinPanel.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/sinfo/FlowDepthTwinPanel.java	Wed Jul 04 18:28:08 2018 +0200
@@ -27,6 +27,6 @@
     private static final NilDatacageTwinPanelInfo RIGHT_INFO = new NilDatacageTwinPanelInfo(MSG.sinfo_columnlabel_waterlevels(), "sinfo_flowdepth_waterlevels");
 
     public FlowDepthTwinPanel(final User user, final int maxCount, final String msgMaxCount) {
-        super(user, new FlowDepthTwinPanelValidator(), LEFT_INFO, RIGHT_INFO, maxCount, msgMaxCount);
+        super(user, new FlowDepthTwinPanelValidator(), new WaterlevelSoundingSelectionController(MSG), LEFT_INFO, RIGHT_INFO, maxCount, msgMaxCount);
     }
 }
\ 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/ui/sinfo/WaterlevelSoundingSelectionController.java	Wed Jul 04 18:28:08 2018 +0200
@@ -0,0 +1,143 @@
+/** 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.ui.sinfo;
+
+import java.util.List;
+
+import org.dive4elements.river.client.client.FLYSConstants;
+import org.dive4elements.river.client.client.ui.DatacagePairWidget.IDatacagePairControler;
+import org.dive4elements.river.client.client.ui.DatacageWidget;
+
+import com.smartgwt.client.data.DataSource;
+import com.smartgwt.client.data.DataSourceField;
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.FieldType;
+import com.smartgwt.client.types.TitleOrientation;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.events.ItemChangedEvent;
+import com.smartgwt.client.widgets.form.events.ItemChangedHandler;
+import com.smartgwt.client.widgets.grid.events.SelectionUpdatedEvent;
+import com.smartgwt.client.widgets.grid.events.SelectionUpdatedHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.tree.TreeNode;
+
+/**
+ * Implements special behavior when selecting soundings and waterlevels: datacage of waterlevels depends on selection of
+ * sounding.
+ *
+ * @author Gernot Belger
+ */
+final class WaterlevelSoundingSelectionController implements IDatacagePairControler {
+
+    private static final String FIELD_INTELLIGENCE = "field_intelligence";
+
+    static final String ATTRIBUTE_YEAR = "year";
+
+    private final FLYSConstants msg;
+
+    private boolean turnFilterOff;
+
+    public WaterlevelSoundingSelectionController(final FLYSConstants msg) {
+        this.msg = msg;
+    }
+
+    @Override
+    public void setup(final DatacageWidget soundingWidget, final DatacageWidget waterlevelWidget, final HLayout toolbarLayout) {
+
+        soundingWidget.getTreeGrid().addSelectionUpdatedHandler(new SelectionUpdatedHandler() {
+
+            @Override
+            public void onSelectionUpdated(final SelectionUpdatedEvent event) {
+                final List<TreeNode> selection = soundingWidget.getPlainSelection();
+                handleSoundingChanged(waterlevelWidget, selection);
+            }
+        });
+
+        final DynamicForm form = createIntelligenCheckbox();
+        toolbarLayout.addMember(form);
+        form.addItemChangedHandler(new ItemChangedHandler() {
+
+            @Override
+            public void onItemChanged(final ItemChangedEvent event) {
+                final Boolean value = (Boolean) form.getValue(FIELD_INTELLIGENCE);
+                checkboxToggled(value);
+                handleSoundingChanged(waterlevelWidget, soundingWidget.getPlainSelection());
+            }
+        });
+
+        /* init to current selection */
+        handleSoundingChanged(waterlevelWidget, soundingWidget.getPlainSelection());
+    }
+
+    private DynamicForm createIntelligenCheckbox() {
+
+        final DynamicForm form = new DynamicForm();
+        form.setTitlePrefix("");
+        form.setTitleSuffix(": ");
+        form.setTitleAlign(Alignment.LEFT);
+        form.setTitleOrientation(TitleOrientation.LEFT);
+
+        final DataSourceField item = new DataSourceField(FIELD_INTELLIGENCE, FieldType.BOOLEAN);
+        item.setTitle(this.msg.sinfo_deactivate_intelligent_datacord());
+
+        final DataSource source = new DataSource();
+        source.setFields(item);
+        form.setDataSource(source);
+
+        form.setValue(FIELD_INTELLIGENCE, false);
+
+        return form;
+    }
+
+    protected final void checkboxToggled(final Boolean selected) {
+        this.turnFilterOff = selected == null ? false : selected;
+    }
+
+    protected final void handleSoundingChanged(final DatacageWidget waterlevelWidget, final List<TreeNode> selection) {
+
+        if (this.turnFilterOff) {
+            waterlevelWidget.setFilter(DatacageWidget.ACCEPT_ALL_FILTER);
+            waterlevelWidget.getTreeGrid().setEmptyMessage(this.msg.databasket_loading());
+            return;
+        }
+
+        // more than one should never happen, as the have single selection
+        final TreeNode soundingNode = selection.isEmpty() ? null : selection.get(0);
+
+        if (soundingNode == null || soundingNode.getAttribute("factory") == null) {
+            waterlevelWidget.setFilter(DatacageWidget.ACCEPT_NONE_FILTER);
+            waterlevelWidget.getTreeGrid().setEmptyMessage(this.msg.sinfo_sounding_waterlevel_select_waterlevel());
+            return;
+        }
+
+        final Integer soundingYear = getYear(soundingNode);
+
+        if (soundingYear == null) {
+            // should never happen, as we should only show soundings that actually have a year
+            waterlevelWidget.setFilter(DatacageWidget.ACCEPT_NONE_FILTER);
+            waterlevelWidget.getTreeGrid().setEmptyMessage("Der gewählte Wassrspiegel hat keine Informations zum Jahr");
+        } else
+            waterlevelWidget.setFilter(new WaterlevelSoundingYearFilter(soundingYear));
+    }
+
+    private static Integer getYear(final TreeNode soundingNode) {
+        final String year = soundingNode.getAttribute(ATTRIBUTE_YEAR);
+        if (year == null || year.trim().isEmpty())
+            return null;
+
+        try {
+            return Integer.valueOf(year);
+        }
+        catch (final NumberFormatException e) {
+            e.printStackTrace();
+            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/client/ui/sinfo/WaterlevelSoundingYearFilter.java	Wed Jul 04 18:28:08 2018 +0200
@@ -0,0 +1,77 @@
+/** 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.ui.sinfo;
+
+import org.dive4elements.river.client.client.ui.DatacageWidget.DatacageFilter;
+import org.dive4elements.river.client.shared.model.AttrList;
+import org.dive4elements.river.client.shared.model.DataCageNode;
+
+/**
+ * @author Gernot Belger
+ */
+final class WaterlevelSoundingYearFilter implements DatacageFilter {
+
+    private final int soundingYear;
+
+    public WaterlevelSoundingYearFilter(final int soundingYear) {
+        this.soundingYear = soundingYear;
+    }
+
+    @Override
+    public boolean accept(final DataCageNode node) {
+
+        final AttrList attributes = node.getAttributes();
+        if (attributes == null)
+            return true;
+
+        if (!attributes.hasAttribute("factory"))
+            return true;
+
+        final Integer waterlevelYear = getYear(attributes);
+        if (waterlevelYear == null) {
+            // should never happen, we should only show waterlevels that have a year
+            return false;
+        }
+
+        final int diff = Math.abs(this.soundingYear - waterlevelYear);
+
+        return diff <= getMaxYearDifference();
+    }
+
+    private int getMaxYearDifference() {
+
+        if (this.soundingYear < 1918)
+            return 25;
+
+        if (1918 <= this.soundingYear && this.soundingYear < 1958)
+            return 12;
+
+        if (1958 <= this.soundingYear && this.soundingYear < 1998)
+            return 6;
+
+        // > 1998
+        return 3;
+    }
+
+    private static Integer getYear(final AttrList attributes) {
+
+        final String year = attributes.getValue(WaterlevelSoundingSelectionController.ATTRIBUTE_YEAR);
+        if (year == null || year.trim().isEmpty())
+            return null;
+
+        try {
+            return Integer.valueOf(year);
+        }
+        catch (final NumberFormatException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/AttrList.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/AttrList.java	Wed Jul 04 18:28:08 2018 +0200
@@ -8,47 +8,64 @@
 
 package org.dive4elements.river.client.shared.model;
 
-import java.util.List;
+import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.List;
 
-import java.io.Serializable;
-
+// FIXME: who implements a thing like this?? this is a HashMap!!
 public class AttrList implements Serializable
 {
-    protected List<String> keyValues;
+    private static final long serialVersionUID = 1L;
+
+    private List<String> keyValues;
 
     public AttrList() {
         this(5);
     }
 
-    public AttrList(int size) {
-        keyValues = new ArrayList<String>(size*2);
+    public AttrList(final int size) {
+        this.keyValues = new ArrayList<String>(size*2);
     }
 
     public int size() {
-        return keyValues != null ? keyValues.size()/2 : null;
-    }
-
-    public String getKey(int index) {
-        return keyValues.get(index*2);
+        return this.keyValues.size() / 2;
     }
 
-    public String getValue(int index) {
-        return keyValues.get(index*2 + 1);
+    /**
+     * IMPORTANT: necessary for serialization
+     */
+    public void setKeyValues(final List<String> keyValues) {
+        this.keyValues = keyValues;
     }
 
-    public void add(String key, String value) {
-        keyValues.add(key);
-        keyValues.add(value);
+    public String getKey(final int index) {
+        return this.keyValues.get(index*2);
     }
 
-    public boolean hasAttribute(String key) {
-        for (int i = 0, N = keyValues.size(); i < N; i += 2) {
-            if (keyValues.get(i).equals(key)) {
+    public String getValue(final int index) {
+        return this.keyValues.get(index*2 + 1);
+    }
+
+    public String getValue(final String key) {
+        for (int i = 0, N = this.keyValues.size(); i < N; i += 2) {
+            if (this.keyValues.get(i).equals(key)) {
+                return this.keyValues.get(i + 1);
+            }
+        }
+        return null;
+    }
+
+    public void add(final String key, final String value) {
+        this.keyValues.add(key);
+        this.keyValues.add(value);
+    }
+
+    public boolean hasAttribute(final String key) {
+        for (int i = 0, N = this.keyValues.size(); i < N; i += 2) {
+            if (this.keyValues.get(i).equals(key)) {
                 return true;
             }
         }
         return false;
     }
-}
-// 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/shared/model/ToLoad.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/ToLoad.java	Wed Jul 04 18:28:08 2018 +0200
@@ -8,52 +8,46 @@
 
 package org.dive4elements.river.client.shared.model;
 
+import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.HashMap;
-
-import java.io.Serializable;
 
 import com.google.gwt.core.client.GWT;
 
 public class ToLoad implements Serializable
 {
-
-    /** Two strings. */
-    public static class StringTriple {
+    private static class StringTriple {
         public String first;
         public String second;
         public String third;
-        public StringTriple(String first, String second, String third) {
+        public StringTriple(final String first, final String second, final String third) {
             this.first = first;
             this.second = second;
             this.third = third;
         }
         @Override
         public int hashCode() {
-            return first.hashCode() + second.hashCode() + third.hashCode();
+            return this.first.hashCode() + this.second.hashCode() + this.third.hashCode();
         }
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(final Object o) {
             if (!(o instanceof StringTriple)) {
                 return false;
             }
-            StringTriple other = (StringTriple) o;
-            return second.equals(other.second)
-                && first.equals(other.first)
-                && third.equals(other.third);
+            final StringTriple other = (StringTriple) o;
+            return this.second.equals(other.second)
+                    && this.first.equals(other.first)
+                    && this.third.equals(other.third);
         }
     }
-    public static final String SYNTHETIC_KEY = "key-";
-
-    protected Map<String, Map<StringTriple, ArtifactFilter>> artifacts;
 
-    public ToLoad() {
-        artifacts = new HashMap<String, Map<StringTriple, ArtifactFilter>>();
-    }
+    private static final String SYNTHETIC_KEY = "key-";
 
-    public static final String uniqueKey(Map<?, ?> map) {
+    private final Map<String, Map<StringTriple, ArtifactFilter>> artifacts = new HashMap<String, Map<StringTriple, ArtifactFilter>>();
+
+    public static final String uniqueKey(final Map<?, ?> map) {
         int idx = map.size();
 
         String key = SYNTHETIC_KEY + idx;
@@ -64,39 +58,39 @@
     }
 
     public void add(
-         String artifactName,
-         String factory,
-         String out,
-         String name,
-         String ids,
-         String displayName
-     ) {
+            final String artifactName,
+            final String factory,
+            final String out,
+            final String name,
+            final String ids,
+            final String displayName
+            ) {
         add(artifactName, factory, out, name, ids, displayName, null);
-     }
+    }
 
     public void add(
-        String artifactName,
-        String factory,
-        String out,
-        String name,
-        String ids,
-        String displayName,
-        String targetOut
-    ) {
+            String artifactName,
+            final String factory,
+            final String out,
+            final String name,
+            final String ids,
+            final String displayName,
+            final String targetOut
+            ) {
         GWT.log("Adding artifact: " + artifactName + " Factory: " + factory +
                 " Out: " + out + " Name: " + name + " Ids: " + ids +
                 " Display Name: " + displayName + " Target Out: " + targetOut);
 
         if (artifactName == null) {
-            artifactName = uniqueKey(artifacts);
+            artifactName = uniqueKey(this.artifacts);
         }
 
-        Map<StringTriple, ArtifactFilter> artifact = artifacts.get(
-            artifactName);
+        Map<StringTriple, ArtifactFilter> artifact = this.artifacts.get(
+                artifactName);
 
         if (artifact == null) {
             artifact = new HashMap<StringTriple, ArtifactFilter>();
-            artifacts.put(artifactName, artifact);
+            this.artifacts.put(artifactName, artifact);
         }
 
         ArtifactFilter filter = artifact.get(factory);
@@ -110,28 +104,28 @@
     }
 
     public boolean isEmpty() {
-        return artifacts.isEmpty();
+        return this.artifacts.isEmpty();
     }
 
     public List<Recommendation> toRecommendations() {
-        List<Recommendation> recommendations = new ArrayList<Recommendation>();
+        final List<Recommendation> recommendations = new ArrayList<Recommendation>();
 
-        for (Map.Entry<String, Map<StringTriple, ArtifactFilter>> all:
-            artifacts.entrySet()
-        ) {
+        for (final Map.Entry<String, Map<StringTriple, ArtifactFilter>> all:
+            this.artifacts.entrySet()
+                ) {
             String masterArtifact = all.getKey();
 
             if (masterArtifact.startsWith(SYNTHETIC_KEY)) { // system data
                 masterArtifact = null;
             }
 
-            for (Map.Entry<StringTriple, ArtifactFilter> entry:
+            for (final Map.Entry<StringTriple, ArtifactFilter> entry:
                 all.getValue().entrySet()
-            ) {
-                StringTriple triple = entry.getKey();
-                String factory = triple.first;
-                String targetOut = triple.third;
-                ArtifactFilter artifactFilter = entry.getValue();
+                    ) {
+                final StringTriple triple = entry.getKey();
+                final String factory = triple.first;
+                final String targetOut = triple.third;
+                final ArtifactFilter artifactFilter = entry.getValue();
 
                 String                ids;
                 Recommendation.Filter filter;
@@ -145,8 +139,8 @@
                     filter = artifactFilter.toFilter();
                 }
 
-                Recommendation recommendation = new Recommendation(
-                    factory, ids, masterArtifact, filter, targetOut);
+                final Recommendation recommendation = new Recommendation(
+                        factory, ids, masterArtifact, filter, targetOut);
                 recommendation.setDisplayName(triple.second);
 
                 recommendations.add(recommendation);
--- a/gwt-client/src/test/java/test/SimpleRecommendation.java	Wed Jul 04 17:14:16 2018 +0200
+++ b/gwt-client/src/test/java/test/SimpleRecommendation.java	Wed Jul 04 18:28:08 2018 +0200
@@ -69,11 +69,10 @@
         final Recommendation recom2 = new Recommendation(rec2.getFactory(), rec2.getIds(), rec2.getTarget());
         recom2.setDisplayName(rec2.getDisplayName());
         final Artifact[] artifacts = loadMany(new Recommendation[] { recom1, recom2 }, null, collection, serverUrl, locale);
-        final String rec1String = RecommandationUtils.createDataString(artifacts[0].getUuid(), recom1, new NilDatacageTwinPanelInfo("xxxx", "yyy"));
-        final String rec2String = RecommandationUtils.createDataString(artifacts[1].getUuid(), recom2, new NilDatacageTwinPanelInfo("xxxx", "yyy"));
+        final String rec1String = RecommandationUtils.createDataString(artifacts[0].getUuid(), recom1, new NilDatacageTwinPanelInfo(null, "xxxx"));
+        final String rec2String = RecommandationUtils.createDataString(artifacts[1].getUuid(), recom2, new NilDatacageTwinPanelInfo(null, "xxxx"));
         final String combinedIdNeu = rec1String + "#" + rec2String;
         return combinedIdNeu;
-
     }
 
     private final Artifact[] loadMany(final Recommendation[] recoms, final String factory, final Collection collection, final String serverUrl,
@@ -106,8 +105,7 @@
                 }
             }
         }
-        return artifacts.toArray(new Artifact[artifacts.size()]);
 
+        return artifacts.toArray(new Artifact[artifacts.size()]);
     }
-
-}
+}
\ No newline at end of file

http://dive4elements.wald.intevation.org