commit a2415b9f0835819692b1d6b45efee9c7f92a8739
Author: Andreas Kohlbecker <a.kohlbecker@bgbm.org>
Date:   Mon Jun 25 12:39:26 2018 +0200

    ref #7458 initial implementation of a note field at the example of the TaxonNameEditor

diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/component/common/FilterableAnnotationsField.java b/src/main/java/eu/etaxonomy/cdm/vaadin/component/common/FilterableAnnotationsField.java
new file mode 100644
index 00000000..dc888012
--- /dev/null
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/component/common/FilterableAnnotationsField.java
@@ -0,0 +1,225 @@
+/**
+* Copyright (C) 2018 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.vaadin.component.common;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.vaadin.viritin.FilterableListContainer;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
+import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.data.fieldgroup.FieldGroup;
+import com.vaadin.data.util.BeanItemContainer;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.CssLayout;
+import com.vaadin.ui.DefaultFieldFactory;
+import com.vaadin.ui.Field;
+import com.vaadin.ui.ListSelect;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Table.ColumnHeaderMode;
+import com.vaadin.ui.TextArea;
+
+import eu.etaxonomy.cdm.model.common.Annotation;
+import eu.etaxonomy.cdm.model.common.AnnotationType;
+import eu.etaxonomy.cdm.model.common.DefinedTermBase;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.vaadin.util.converter.SetToListConverter;
+import eu.etaxonomy.cdm.vaadin.util.filter.CdmTermFilter;
+import eu.etaxonomy.vaadin.component.CompositeCustomField;
+
+/**
+ * @author a.kohlbecker
+ * @since Jun 20, 2018
+ *
+ */
+public class FilterableAnnotationsField extends CompositeCustomField<List<Annotation>> {
+
+    private static final long serialVersionUID = -8258550787601028813L;
+
+    Class<List<Annotation>> type = (Class<List<Annotation>>)new ArrayList<Annotation>().getClass();
+
+    private CssLayout root = new CssLayout();
+
+    private Table table = new Table();
+
+    private List<AnnotationType> typesFilter = null;
+
+    private Annotation emptyDefaultAnnotation = Annotation.NewInstance(null, Language.DEFAULT());
+
+    private BeanItemContainer<DefinedTermBase> typeSelectItemContainer;
+
+    private FilterableListContainer<Annotation> container;
+
+    public FilterableAnnotationsField() {
+        this(null);
+    }
+
+    public FilterableAnnotationsField(String caption) {
+
+        setCaption(caption);
+        // annotations are always sets
+        setConverter(new SetToListConverter<Annotation>());
+
+        root.setWidth(100, Unit.PERCENTAGE);
+
+        // setup table
+        table.setPageLength(1);
+        table.setColumnHeaderMode(ColumnHeaderMode.HIDDEN);
+        table.setWidth(100,  Unit.PERCENTAGE);
+        table.setTableFieldFactory(new DefaultFieldFactory() {
+
+            private static final long serialVersionUID = 5437750882205859178L;
+
+            @Override
+            public Field<?> createField(Item item, Object propertyId, Component uiContext) {
+
+                Field<?> field = createField(propertyId);
+                if(field == null) {
+                    field = super.createField(item, propertyId, uiContext);
+                }
+                return field;
+
+            }
+
+            @Override
+            public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) {
+
+                Field<?> field = createField(propertyId);
+
+                if(field == null) {
+                    field = super.createField(container, itemId, propertyId, uiContext);
+                }
+                return field;
+            }
+
+            protected Field<?> createField(Object propertyId) {
+                Field<?> field = null;
+                if(propertyId.equals("text")){
+                    TextArea ta = new TextArea();
+                    ta.setNullRepresentation("");
+                    ta.setWidth(100,  Unit.PERCENTAGE);
+                    field = ta;
+                } else if(propertyId.equals("annotationType")) {
+                    ListSelect select = new ListSelect();
+                    select.setContainerDataSource(typeSelectItemContainer);
+                    select.setWidth(100, Unit.PIXELS);
+                    select.setRows(1);
+                    field = select;
+                }
+                field.setStyleName(table.getStyleName());
+                return field;
+            }
+        });
+
+        addStyledComponent(table);
+
+    }
+
+    public void setAnnotationTypesVisible(AnnotationType ... types){
+        typesFilter = Arrays.asList(types);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addDefaultStyles() {
+        // no default styles here
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FieldGroup getFieldGroup() {
+        // holds a Container instead // TODO can this cause a NPE?
+        return null;
+    }
+
+    @Override
+    public void commit() throws SourceException, InvalidValueException {
+        table.commit();
+        Collection<Filter> filters = container.getContainerFilters();
+        super.commit();
+        for(Filter filter : filters){
+            container.addContainerFilter(filter);
+        }
+        System.err.println(container.size());
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected List<Annotation> getInternalValue() {
+        if(container == null || container.getItemIds() == null){
+            return null;
+        }
+        return new ArrayList<>(container.getItemIds());
+    }
+
+
+    @Override
+    protected void setInternalValue(List<Annotation> newValue) {
+        boolean hasIncludeFilter = typesFilter != null && !typesFilter.isEmpty();
+        boolean onlyOneType = hasIncludeFilter && typesFilter.size() == 1;
+
+        if(newValue.isEmpty()){
+            newValue.add(emptyDefaultAnnotation);
+            if(onlyOneType){
+                emptyDefaultAnnotation.setAnnotationType(typesFilter.get(0));
+            }
+        }
+        container = new FilterableListContainer<Annotation>(newValue);
+        if(hasIncludeFilter){
+            container.addContainerFilter(new CdmTermFilter<AnnotationType>("annotationType", typesFilter));
+        }
+        table.setContainerDataSource(container);
+        if(onlyOneType){
+            table.setVisibleColumns("text");
+        } else {
+            table.setVisibleColumns("text", "annotationType");
+        }
+        table.setEditable(true);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Component initContent() {
+        root.addComponent(table);
+        return root;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Class<List<Annotation>> getType() {
+        return type;
+    }
+
+    /**
+     * @param buildTermItemContainer
+     */
+    public void setAnnotationTypeItemContainer(BeanItemContainer<DefinedTermBase> typeSelectItemContainer) {
+        this.typeSelectItemContainer = typeSelectItemContainer;
+    }
+
+
+}
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/model/name/TaxonNameDTO.java b/src/main/java/eu/etaxonomy/cdm/vaadin/model/name/TaxonNameDTO.java
index 6340a28d..84f0bad2 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/model/name/TaxonNameDTO.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/model/name/TaxonNameDTO.java
@@ -8,6 +8,7 @@
 */
 package eu.etaxonomy.cdm.vaadin.model.name;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -65,6 +66,25 @@ public class TaxonNameDTO extends CdmEntityDecoraterDTO<TaxonName> {
         return name.getAnnotations();
     }
 
+    public void setAnnotations(Set<Annotation> annotations) {
+        List<Annotation> currentAnnotations = new ArrayList<>(name.getAnnotations());
+        List<Annotation> annotationsSeen = new ArrayList<>();
+        for(Annotation a : annotations){
+            if(a == null){
+                continue;
+            }
+            if(!currentAnnotations.contains(a)){
+                name.addAnnotation(a);
+            }
+            annotationsSeen.add(a);
+        }
+        for(Annotation a : currentAnnotations){
+            if(!annotationsSeen.contains(a)){
+                name.removeAnnotation(a);
+            }
+        }
+    }
+
     public String getAppendedPhrase() {
         return name.getAppendedPhrase();
     }
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/util/filter/CdmTermFilter.java b/src/main/java/eu/etaxonomy/cdm/vaadin/util/filter/CdmTermFilter.java
new file mode 100644
index 00000000..3eeb7c45
--- /dev/null
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/util/filter/CdmTermFilter.java
@@ -0,0 +1,60 @@
+/**
+* Copyright (C) 2018 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.vaadin.util.filter;
+
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+
+import eu.etaxonomy.cdm.model.common.TermBase;
+
+/**
+ * @author a.kohlbecker
+ * @since Jun 21, 2018
+ *
+ */
+public class CdmTermFilter<T extends TermBase> implements Filter {
+
+    private static final long serialVersionUID = -613582375956129270L;
+
+    private List<T> includeFilter;
+    private Object propertyId;
+
+    /**
+     *
+     * @param propertyId
+     * @param includeFilter
+     * @param includeNullValues true by default
+     */
+    public CdmTermFilter(Object propertyId, List<T> includeFilter){
+        this.propertyId = propertyId;
+        this.includeFilter = includeFilter;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException {
+        Property property = item.getItemProperty(propertyId);
+        Object value = property.getValue();
+        if(includeFilter.contains(value)){
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean appliesToProperty(Object propertyId) {
+        return this.propertyId.equals(propertyId);
+    }
+
+}
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNameEditorPresenter.java b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNameEditorPresenter.java
index 313fa2bc..7c1420dd 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNameEditorPresenter.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNameEditorPresenter.java
@@ -27,6 +27,7 @@ import eu.etaxonomy.cdm.api.service.INameService;
 import eu.etaxonomy.cdm.model.agent.AgentBase;
 import eu.etaxonomy.cdm.model.agent.Person;
 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
+import eu.etaxonomy.cdm.model.common.AnnotationType;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.TermType;
 import eu.etaxonomy.cdm.model.name.Rank;
@@ -175,6 +176,9 @@ public class TaxonNameEditorPresenter extends AbstractCdmDTOEditorPresenter<Taxo
         getView().getValidationField().getCitatonComboBox().getSelect().setCaptionGenerator(new CdmTitleCacheCaptionGenerator<Reference>());
         getView().getValidationField().getCitatonComboBox().loadFrom(icbnCodesPagingProvider, icbnCodesPagingProvider, icbnCodesPagingProvider.getPageSize());
         getView().getValidationField().getCitatonComboBox().getSelect().addValueChangeListener(new ToOneRelatedEntityReloader<>(getView().getValidationField().getCitatonComboBox(), this));
+
+        getView().getAnnotationsField().setAnnotationTypeItemContainer(selectFieldFactory.buildTermItemContainer(
+                AnnotationType.EDITORIAL().getUuid(), AnnotationType.TECHNICAL().getUuid()));
     }
 
     /**
@@ -183,9 +187,10 @@ public class TaxonNameEditorPresenter extends AbstractCdmDTOEditorPresenter<Taxo
     @Override
     protected TaxonName loadCdmEntity(UUID identifier) {
 
-        List<String> initStrategy = Arrays.asList(new String []{
-
+        List<String> initStrategy = Arrays.asList(
                 "$",
+                "annotations.type",
+                "annotations.*", // needed as log as we are using a table in FilterableAnnotationsField
                 "rank.vocabulary", // needed for comparing ranks
 
                 "nomenclaturalReference.authorship",
@@ -214,8 +219,6 @@ public class TaxonNameEditorPresenter extends AbstractCdmDTOEditorPresenter<Taxo
 
                 "relationsFromThisName",
                 "homotypicalGroup.typifiedNames"
-
-                }
         );
 
         TaxonName taxonName;
@@ -255,7 +258,6 @@ public class TaxonNameEditorPresenter extends AbstractCdmDTOEditorPresenter<Taxo
 
             }
         }
-
         return taxonName;
     }
 
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditor.java b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditor.java
index 44dd9fa1..8fc0621d 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditor.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditor.java
@@ -34,6 +34,7 @@ import com.vaadin.ui.ListSelect;
 import com.vaadin.ui.TextField;
 
 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
+import eu.etaxonomy.cdm.model.common.AnnotationType;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
@@ -41,6 +42,7 @@ import eu.etaxonomy.cdm.model.name.Rank;
 import eu.etaxonomy.cdm.model.name.TaxonName;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.vaadin.component.TextFieldNFix;
+import eu.etaxonomy.cdm.vaadin.component.common.FilterableAnnotationsField;
 import eu.etaxonomy.cdm.vaadin.component.common.TeamOrPersonField;
 import eu.etaxonomy.cdm.vaadin.event.ReferenceEditorAction;
 import eu.etaxonomy.cdm.vaadin.event.TaxonNameEditorAction;
@@ -65,13 +67,14 @@ import eu.etaxonomy.vaadin.mvp.AbstractCdmDTOPopupEditor;
  */
 @SpringComponent
 @Scope("prototype")
-public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO, TaxonName, TaxonNameEditorPresenter> implements TaxonNamePopupEditorView, AccessRestrictedView {
+public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO, TaxonName, TaxonNameEditorPresenter>
+    implements TaxonNamePopupEditorView, AccessRestrictedView {
 
     private static final long serialVersionUID = -7037436241474466359L;
 
     private final static int GRID_COLS = 4;
 
-    private final static int GRID_ROWS = 16;
+    private final static int GRID_ROWS = 17;
 
     private static final boolean HAS_BASIONYM_DEFAULT = false;
 
@@ -129,6 +132,30 @@ public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO
 
     private ValueChangeListener updateFieldVisibilityListener = e -> updateFieldVisibility();
 
+    private FilterableAnnotationsField annotationsListField;
+
+    private AnnotationType[] editableAnotationTypes = new AnnotationType[]{AnnotationType.EDITORIAL()};
+
+
+    /**
+     * By default  AnnotationType.EDITORIAL() is enabled.
+     *
+     * @return the editableAnotationTypes
+     */
+    public AnnotationType[] getEditableAnotationTypes() {
+        return editableAnotationTypes;
+    }
+
+    /**
+     * By default  AnnotationType.EDITORIAL() is enabled.
+     *
+     *
+     * @param editableAnotationTypes the editableAnotationTypes to set
+     */
+    public void setEditableAnotationTypes(AnnotationType ... editableAnotationTypes) {
+        this.editableAnotationTypes = editableAnotationTypes;
+    }
+
     /**
      * @param layout
      * @param dtoType
@@ -146,7 +173,6 @@ public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO
     }
 
 
-
     /**
      * {@inheritDoc}
      */
@@ -420,6 +446,12 @@ public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO
         exCombinationAuthorshipField.setWidth(100,  Unit.PERCENTAGE);
         addField(exCombinationAuthorshipField, "exCombinationAuthorship", 0, row, GRID_COLS-1, row);
 
+        row++;
+        annotationsListField = new FilterableAnnotationsField("Editorial notes");
+        annotationsListField.setWidth(100, Unit.PERCENTAGE);
+        annotationsListField.setAnnotationTypesVisible(editableAnotationTypes);
+        addField(annotationsListField, "annotations", 0, row, GRID_COLS-1, row);
+
         // -----------------------------------------------------------------------------
 
         setAdvancedModeEnabled(true);
@@ -831,6 +863,11 @@ public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO
         return basionymToggle;
     }
 
+    @Override
+    public FilterableAnnotationsField getAnnotationsField() {
+        return annotationsListField;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -852,6 +889,8 @@ public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO
         }
     }
 
+
+
     /**
      * @return the infraGenericEpithetField
      */
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditorView.java b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditorView.java
index 64454e33..b5b8a9d8 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditorView.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditorView.java
@@ -16,6 +16,7 @@ import com.vaadin.ui.ListSelect;
 
 import eu.etaxonomy.cdm.model.name.TaxonName;
 import eu.etaxonomy.cdm.model.reference.Reference;
+import eu.etaxonomy.cdm.vaadin.component.common.FilterableAnnotationsField;
 import eu.etaxonomy.cdm.vaadin.component.common.TeamOrPersonField;
 import eu.etaxonomy.vaadin.component.NameRelationField;
 import eu.etaxonomy.vaadin.component.ToManyRelatedEntitiesComboboxSelect;
@@ -115,4 +116,9 @@ public interface TaxonNamePopupEditorView extends ApplicationView<TaxonNameEdito
      */
     public AbstractField<String> getInfraSpecificEpithetField();
 
+    /**
+     * @return
+     */
+    FilterableAnnotationsField getAnnotationsField();
+
 }
diff --git a/src/main/java/eu/etaxonomy/vaadin/component/CompositeCustomField.java b/src/main/java/eu/etaxonomy/vaadin/component/CompositeCustomField.java
index 8ae85619..df31932f 100644
--- a/src/main/java/eu/etaxonomy/vaadin/component/CompositeCustomField.java
+++ b/src/main/java/eu/etaxonomy/vaadin/component/CompositeCustomField.java
@@ -29,6 +29,17 @@ import eu.etaxonomy.cdm.vaadin.event.NestedButtonStateUpdater;
 /**
  * TODO implement height methods for full component size support
  *
+ * Implementations need to override {@link  AbstractField#setInternalValue(TeamOrPersonBase<?> newValue)} in order to
+ * to set the item datasource of the fieldGroup for example:
+ * <pre>
+ * @Override
+   protected void setInternalValue(TeamOrPersonBase<?> newValue) {
+     ...
+     fieldGroup.setItemDataSource(new BeanItem<Team>((Team)newValue));
+     ...
+   }
+ * </pre>
+ *
  * @author a.kohlbecker
  * @since May 12, 2017
  *
diff --git a/src/main/java/eu/etaxonomy/vaadin/component/NestedFieldGroup.java b/src/main/java/eu/etaxonomy/vaadin/component/NestedFieldGroup.java
index b05851d9..27781fbc 100644
--- a/src/main/java/eu/etaxonomy/vaadin/component/NestedFieldGroup.java
+++ b/src/main/java/eu/etaxonomy/vaadin/component/NestedFieldGroup.java
@@ -20,7 +20,7 @@ public interface NestedFieldGroup {
     /**
      * Implementations return the local fieldGroup
      *
-     * @return
+     * @return the FieldGroup, may be <code>null</code>.
      */
     public abstract FieldGroup getFieldGroup();
 
