From 199c8154316016aa1a22c2b909b957b809c517b4 Mon Sep 17 00:00:00 2001
From: Andreas Bayha <bayha@fortiss.org>
Date: Wed, 22 Jul 2020 15:03:19 +0200
Subject: [PATCH] Annotations: Improved code

- The filter is now better integrated with the content provider.
- Javadoc comments.

Issue-Ref: 4014
Issue-Url: https://af3-developer.fortiss.org/issues/4014
Signed-off-by: Andreas Bayha <bayha@fortiss.org>
---
 .../annotation/view/fx/AnnotationViewFx.fxml  |   9 +-
 .../base/ui/annotation/view/fx/.ratings       |   4 +-
 .../view/fx/AnnotationViewFXController.java   | 290 ++++++++++--------
 ...=> FXAnnotationFilterContentProvider.java} | 103 +++++--
 4 files changed, 234 insertions(+), 172 deletions(-)
 rename org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/{FXAnnotationFilter.java => FXAnnotationFilterContentProvider.java} (72%)

diff --git a/org.fortiss.tooling.base.ui/res/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFx.fxml b/org.fortiss.tooling.base.ui/res/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFx.fxml
index 7e318bd67..10a7f77d9 100644
--- a/org.fortiss.tooling.base.ui/res/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFx.fxml
+++ b/org.fortiss.tooling.base.ui/res/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFx.fxml
@@ -6,7 +6,6 @@
 <?import javafx.scene.control.RadioButton?>
 <?import javafx.scene.control.SplitPane?>
 <?import javafx.scene.control.TextField?>
-<?import javafx.scene.control.ToggleGroup?>
 <?import javafx.scene.control.TreeTableView?>
 <?import javafx.scene.layout.AnchorPane?>
 <?import javafx.scene.layout.HBox?>
@@ -27,12 +26,8 @@
                         <HBox prefHeight="40.0" prefWidth="148.0" spacing="5.0">
                            <children>
                               <Label minHeight="20.0" text="Filter:" />
-                              <RadioButton fx:id="radBtnFilterModelElements" minHeight="20.0" mnemonicParsing="false" text="model elements">
-                                 <toggleGroup>
-                                    <ToggleGroup fx:id="filterGroup" />
-                                 </toggleGroup>
-                              </RadioButton>
-                              <RadioButton fx:id="radBtnFilterAnnotationNames" minHeight="20.0" mnemonicParsing="false" text="annotation names:" toggleGroup="$filterGroup" />
+                              <RadioButton fx:id="radBtnFilterModelElements" minHeight="20.0" mnemonicParsing="false" text="model elements" />
+                              <RadioButton fx:id="radBtnFilterAnnotationNames" minHeight="20.0" mnemonicParsing="false" text="annotation names:" />
                               <CheckBox fx:id="chkBoxMatchCase" mnemonicParsing="false" text="match case" />
                            </children>
                         </HBox>
diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/.ratings
index 53fade13b..649d4f77a 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/.ratings
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/.ratings
@@ -1,3 +1,3 @@
 AnnotationFxViewPart.java 34c845f7162a8a8bb680df6ee08160ce63a5a5a3 YELLOW
-AnnotationViewFXController.java 50e745f4f700b165c3d138d9f9cd2463252da31d RED
-FXAnnotationFilter.java 05bc78a530becbce573545d67e5b0ef9ac3f697f YELLOW
+AnnotationViewFXController.java 00c64dbad1bd9ae071360ebe3d071705c8d2a68c YELLOW
+FXAnnotationFilterContentProvider.java 6c92d9c96fb2a2441256f70e20a20b1287018a9f YELLOW
diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java
index 50e745f4f..00c64dbad 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/AnnotationViewFXController.java
@@ -17,16 +17,15 @@ package org.fortiss.tooling.base.ui.annotation.view.fx;
 
 import static java.util.Collections.emptyList;
 import static java.util.stream.Collectors.toList;
-import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilter.HIERARCHY_LEVELS_ALL;
-import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilter.HIERARCHY_LEVELS_CURRENT;
-import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilter.HIERARCHY_LEVELS_SELECTED_SUBMODEL;
+import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilterContentProvider.HIERARCHY_LEVELS_ALL;
+import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilterContentProvider.HIERARCHY_LEVELS_CURRENT;
+import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilterContentProvider.HIERARCHY_LEVELS_SELECTED_SUBMODEL;
 import static org.fortiss.tooling.kernel.ui.util.SelectionUtils.checkAndPickFirst;
 import static org.fortiss.tooling.kernel.utils.EcoreUtils.getChildrenWithType;
 import static org.fortiss.tooling.kernel.utils.EcoreUtils.getFirstParentWithType;
 import static org.fortiss.tooling.kernel.utils.LoggingUtils.showWarning;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -52,7 +51,6 @@ import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
 import org.fortiss.tooling.base.model.element.IModelElement;
 import org.fortiss.tooling.base.ui.annotation.view.generic.ColumnHandle;
 import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTextFieldTreeTableCell;
-import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeContentProviderBase;
 import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeTableUIProviderBase;
 import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeTableViewer;
 import org.fortiss.tooling.common.ui.javafx.layout.CompositeFXControllerBase;
@@ -61,6 +59,8 @@ import org.fortiss.tooling.kernel.model.INamedCommentedElement;
 import org.fortiss.tooling.kernel.model.IProjectRootElement;
 import org.fortiss.tooling.kernel.service.IPersistencyService;
 
+import com.sun.webkit.ThemeClient;
+
 import javafx.collections.ObservableList;
 import javafx.fxml.FXML;
 import javafx.scene.Node;
@@ -74,6 +74,7 @@ import javafx.scene.control.TreeTableColumn;
 import javafx.scene.control.TreeTableView;
 
 /**
+ * Controller for the Annotations view.
  * 
  * @author bayha
  */
@@ -81,56 +82,76 @@ import javafx.scene.control.TreeTableView;
 public class AnnotationViewFXController extends CompositeFXControllerBase<SplitPane, Node>
 		implements ISelectionListener {
 
+	/** Option text for the annotation type filter combo box. */
 	private static final String SHOW_ALL_ANNOTATION_TYPES = "Show all annotation types";
 
+	/** {@link TextField} for entering the filter pattern. */
 	@FXML
 	TextField txtFilterText;
 
+	/** {@link RadioButton} to enable filtering for model element names. */
 	@FXML
 	RadioButton radBtnFilterModelElements;
 
+	/** {@link RadioButton} to enable filtering for annotation names. */
 	@FXML
 	RadioButton radBtnFilterAnnotationNames;
 
+	/** {@link CheckBox} to make name filtering case sensitive. */
 	@FXML
 	CheckBox chkBoxMatchCase;
 
+	/** {@link CheckBox} to only show model elements with the same type as the selected one. */
 	@FXML
 	CheckBox chkBoxselectedTypeOnly;
 
+	/**
+	 * {@link ComboBox} to chose the hierarchy option according to which the modele elements are
+	 * displayed.
+	 */
 	@FXML
 	ComboBox<String> comboHierarchy;
 
+	/** {@link ComboBox} to chose an annotation type to be displayed exclusively. */
 	@FXML
 	ComboBox<String> comboAnnotationType;
 
-	/**  */
+	/** The {@link TreeTableView} for the annotation table. */
 	@FXML
 	private TreeTableView<AnnotationEntry> annotationTreeTableView;
 
-	/**  */
+	/** The {@link DynamicTreeTableViewer} to manage the annotationTreeTableView. */
 	private DynamicTreeTableViewer<AnnotationEntry> annotationViewer;
 
-	/**  */
+	/** The {@link IProjectRootElement} in which the selected element is contained in. */
 	private IProjectRootElement curentRootElement;
 
+	/** The {@link IModelElement} that is currently selected by the user. */
 	IModelElement selected;
 
-	private FXAnnotationFilter filter = new FXAnnotationFilter();
-
+	/** {@link Set} of {@link ColumnHandle}s (i.e. management objects for columns). */
 	private Set<ColumnHandle<IAnnotatedSpecification>> columnHandles = new TreeSet<>();
 
-	private Map<ColumnHandle<IAnnotatedSpecification>, TreeTableColumn<AnnotationEntry, ?>> columnHandleToColumn = new HashMap<ColumnHandle<IAnnotatedSpecification>, TreeTableColumn<AnnotationEntry, ?>>();
+	/**
+	 * {@link Map} that maps {@link ColumnHandle}s (i.e. management objects for columns) to the
+	 * respective columns.
+	 */
+	private Map<ColumnHandle<IAnnotatedSpecification>, TreeTableColumn<AnnotationEntry, ?>> columnHandleToColumn =
+			new HashMap<ColumnHandle<IAnnotatedSpecification>, TreeTableColumn<AnnotationEntry, ?>>();
 
-	private Map<String, IAnnotatedSpecification> colNameToSpecification = new HashMap<String, IAnnotatedSpecification>();
+	/** {@link Map} to get the {@link IAnnotatedSpecification} that corresponds to a column name. */
+	private Map<String, IAnnotatedSpecification> colNameToSpecification =
+			new HashMap<String, IAnnotatedSpecification>();
 
-	DynamicTreeContentProviderBase<AnnotationEntry> contentProvider;
+	/** Content provider for the annotation table with all filtering functionality. */
+	FXAnnotationFilterContentProvider filterContentProvider =
+			new FXAnnotationFilterContentProvider();
 
 	/**
 	 * Associates the handles of the annotations with their ordered column indexes.
 	 * Used for the UI providers of cell items.
 	 */
-	Map<Integer, ColumnHandle<IAnnotatedSpecification>> colIdxAnnotationMap = new HashMap<>();;
+	Map<Integer, ColumnHandle<IAnnotatedSpecification>> colIdxAnnotationMap = new HashMap<>();
 
 	/** {@inheritDoc} */
 	@Override
@@ -149,18 +170,18 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
 		EObject selected = checkAndPickFirst(selection, EObject.class);
 
-		if (selected == null) {
+		if(selected == null) {
 			clearTableViewer();
 			return;
 		}
 
-		if (selected instanceof IProjectRootElement) {
-			curentRootElement = (IProjectRootElement) selected;
+		if(selected instanceof IProjectRootElement) {
+			curentRootElement = (IProjectRootElement)selected;
 		} else {
 			curentRootElement = getFirstParentWithType(selected, IProjectRootElement.class);
 		}
 
-		if (curentRootElement == null) {
+		if(curentRootElement == null) {
 			// There was no root element for the selected element. (Usually should be the
 			// FileProject.) This is not displayed here.
 			clearTableViewer();
@@ -168,9 +189,9 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 			return;
 		}
 
-		if (selected instanceof IModelElement) {
-			filter.setCurrentySelectedModelElement((IModelElement) selected);
-			this.selected = (IModelElement) selected;
+		if(selected instanceof IModelElement) {
+			filterContentProvider.setCurrentySelectedModelElement((IModelElement)selected);
+			this.selected = (IModelElement)selected;
 		}
 
 		setupAnnotationTree();
@@ -180,7 +201,8 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		ObservableList<String> annotationTypeOptions = comboAnnotationType.getItems();
 		annotationTypeOptions.clear();
 		annotationTypeOptions.add(SHOW_ALL_ANNOTATION_TYPES);
-		List<String> annotations = columnHandles.stream().map(h -> h.getColumnName()).collect(toList());
+		List<String> annotations =
+				columnHandles.stream().map(h -> h.getColumnName()).collect(toList());
 		annotationTypeOptions.addAll(annotations);
 		comboAnnotationType.setValue(SHOW_ALL_ANNOTATION_TYPES);
 	}
@@ -190,34 +212,36 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 	 * selectedElement.
 	 */
 	private void setupAnnotationTree() {
-		if (curentRootElement == null) {
+		if(curentRootElement == null) {
 			return;
 		}
 
 		clearTableViewer();
 
 		// Collect the annotation entries for all elements.
-		EList<IModelElement> childElements = getChildrenWithType(curentRootElement, IModelElement.class);
+		EList<IModelElement> childElements =
+				getChildrenWithType(curentRootElement, IModelElement.class);
 
 		ArtificialRoot rootElement = new ArtificialRoot();
 
-		for (IModelElement elem : childElements) {
+		for(IModelElement elem : childElements) {
 			AnnotationEntry entry = IAnnotationValueService.getInstance().getAnnotationEntry(elem);
 
-			if (entry != null && !entry.getSpecificationsList().isEmpty()) {
+			if(entry != null && !entry.getSpecificationsList().isEmpty()) {
 				rootElement.elements.add(entry);
 			}
 		}
 
 		Set<AnnotationEntry> annotationEntries = new HashSet<>();
-		childElements.forEach(e -> annotationEntries.add(AnnotationValueService.getInstance().getAnnotationEntry(e)));
+		childElements.forEach(e -> annotationEntries
+				.add(AnnotationValueService.getInstance().getAnnotationEntry(e)));
 
 		// Aggregate required columns. Column order is defined by
 		// ColumnHandle.compareTo().
-		for (AnnotationEntry entry : annotationEntries) {
-			for (IAnnotatedSpecification spec : entry.getSpecificationsList()) {
-				ColumnHandle<IAnnotatedSpecification> columnHandle = new ColumnHandle<IAnnotatedSpecification>(entry,
-						spec);
+		for(AnnotationEntry entry : annotationEntries) {
+			for(IAnnotatedSpecification spec : entry.getSpecificationsList()) {
+				ColumnHandle<IAnnotatedSpecification> columnHandle =
+						new ColumnHandle<IAnnotatedSpecification>(entry, spec);
 				columnHandles.add(columnHandle);
 				colNameToSpecification.put(columnHandle.getColumnName(), spec);
 			}
@@ -226,16 +250,17 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		// The DynamicTreeTableViewer needs to be created new, as the root is different.
 		boolean showRoot = false;
 		int revealLevel = 1;
-		DynamicTreeTableUIProviderBase<AnnotationEntry> uiProvider = new AnnotationTreeTableUIProvider();
-		contentProvider = new AnnotationTableContentProvider();
-		annotationViewer = new DynamicTreeTableViewer<AnnotationEntry>(annotationTreeTableView, rootElement, showRoot,
-				revealLevel, contentProvider, uiProvider);
+		DynamicTreeTableUIProviderBase<AnnotationEntry> uiProvider =
+				new AnnotationTreeTableUIProvider();
+
+		annotationViewer = new DynamicTreeTableViewer<AnnotationEntry>(annotationTreeTableView,
+				rootElement, showRoot, revealLevel, filterContentProvider, uiProvider);
 
 		annotationViewer.addColumn("Model Element", 200);
 		annotationViewer.addColumn("Comment", 250);
 
 		int columnIdx = 2;
-		for (ColumnHandle<IAnnotatedSpecification> handle : columnHandles) {
+		for(ColumnHandle<IAnnotatedSpecification> handle : columnHandles) {
 			TreeTableColumn<AnnotationEntry, ?> col = createColumnForHandle(columnIdx, handle);
 			columnHandleToColumn.put(handle, col);
 
@@ -244,6 +269,7 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		filterColumns();
 	}
 
+	/** Resets {@link ThemeClient} annotation table to be empty. Also removes the columns. */
 	private void clearTableViewer() {
 		colIdxAnnotationMap.clear();
 		columnHandles.clear();
@@ -252,10 +278,11 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		annotationTreeTableView.setRoot(null);
 	}
 
+	/** Configures the filter widgets for the annotation table. */
 	private void setupFilterWidgets() {
 		comboAnnotationType.getItems().add(SHOW_ALL_ANNOTATION_TYPES);
 		comboAnnotationType.setValue(SHOW_ALL_ANNOTATION_TYPES);
-		
+
 		ObservableList<String> hierarchyLevels = comboHierarchy.getItems();
 		hierarchyLevels.clear();
 		hierarchyLevels.add(HIERARCHY_LEVELS_ALL);
@@ -264,21 +291,21 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		comboHierarchy.setValue(HIERARCHY_LEVELS_ALL);
 
 		comboHierarchy.valueProperty().addListener((obs, oVal, nVal) -> {
-			filter.setHierarchyLevelFilter(nVal);
+			filterContentProvider.setHierarchyLevelFilter(nVal);
 
 			updateAnnotationTree();
 		});
 
 		comboAnnotationType.valueProperty().addListener((obs, oVal, nVal) -> {
-			filter.setHierarchyLevelFilter(nVal);
+			filterContentProvider.setHierarchyLevelFilter(nVal);
 
 			updateAnnotationTree();
 		});
 
 		txtFilterText.textProperty().addListener((obs, oVal, nVal) -> {
-			filter.setNameFilterPattern(nVal);
+			filterContentProvider.setFilterExpression(nVal);
 
-			if (radBtnFilterAnnotationNames.isSelected()) {
+			if(radBtnFilterAnnotationNames.isSelected()) {
 				filterColumns();
 			}
 
@@ -286,23 +313,33 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		});
 
 		radBtnFilterAnnotationNames.selectedProperty().addListener((obs, oVal, nVal) -> {
-			filter.setFilterColumnName(nVal);
+			filterContentProvider.setFilterColumnName(nVal);
 
 			filterColumns();
 
+			if(nVal) {
+				// De-select other radio button, if this one is selected.
+				radBtnFilterModelElements.setSelected(false);
+			}
+
 			updateAnnotationTree();
 		});
 
 		radBtnFilterModelElements.selectedProperty().addListener((obs, oVal, nVal) -> {
-			filter.setFilterRowName(nVal);
+			filterContentProvider.setFilterRowName(nVal);
+
+			if(nVal) {
+				// De-select other radio button, if this one is selected.
+				radBtnFilterAnnotationNames.setSelected(false);
+			}
 
 			updateAnnotationTree();
 		});
 
 		chkBoxMatchCase.selectedProperty().addListener((obs, oVal, nVal) -> {
-			filter.setFilterNameMatchCase(nVal);
+			filterContentProvider.setFilterNameMatchCase(nVal);
 
-			if (radBtnFilterAnnotationNames.isSelected()) {
+			if(radBtnFilterAnnotationNames.isSelected()) {
 				filterColumns();
 			}
 
@@ -310,18 +347,18 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		});
 
 		chkBoxselectedTypeOnly.selectedProperty().addListener((obs, oVal, nVal) -> {
-			filter.setRestrictToSelectedModelElementType(nVal);
+			filterContentProvider.setRestrictToSelectedModelElementType(nVal);
 
 			updateAnnotationTree();
 		});
 
 		comboAnnotationType.valueProperty().addListener((obs, oVal, nVal) -> {
 			IAnnotatedSpecification selectedSpec = colNameToSpecification.get(nVal);
-			if (selectedSpec != null) {
-				filter.setAnnotationTypeFilter(selectedSpec.getClass());
+			if(selectedSpec != null) {
+				filterContentProvider.setAnnotationTypeFilter(selectedSpec.getClass());
 			} else {
 				// In this case, the filter has been reset to "All Types"
-				filter.setAnnotationTypeFilter(null);
+				filterContentProvider.setAnnotationTypeFilter(null);
 			}
 
 			filterColumns();
@@ -329,15 +366,20 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		});
 	}
 
+	/**
+	 * Triggers the update the annotation tree with the latest filter settings and model element
+	 * changes.
+	 */
 	private void updateAnnotationTree() {
 		annotationViewer.update();
 		annotationViewer.expandAllItems();
 	}
 
+	/** Hides columns which are de-selected by the current filter settings. */
 	private void filterColumns() {
-		for (ColumnHandle<IAnnotatedSpecification> c : columnHandles) {
+		for(ColumnHandle<IAnnotatedSpecification> c : columnHandles) {
 			TreeTableColumn<AnnotationEntry, ?> col = columnHandleToColumn.get(c);
-			col.setVisible(filter.passesColumnFilter(c));
+			col.setVisible(filterContentProvider.passesColumnFilter(c));
 		}
 
 		updateAnnotationTree();
@@ -346,13 +388,14 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 	/** Creates the annotation column for the given handle. */
 	private TreeTableColumn<AnnotationEntry, ?> createColumnForHandle(int columnIdx,
 			ColumnHandle<IAnnotatedSpecification> handle) {
-		Class<IAnnotatedSpecification> annotationClass = (Class<IAnnotatedSpecification>) handle
-				.getAnnotatedSpecification().getClass();
+		Class<IAnnotatedSpecification> annotationClass =
+				(Class<IAnnotatedSpecification>)handle.getAnnotatedSpecification().getClass();
 		AnnotationEntry entry = handle.getEntry();
-		IAnnotationValueProvider<IAnnotatedSpecification> valueProvider = entry
-				.getAnnotationValueProvider(annotationClass);
+		IAnnotationValueProvider<IAnnotatedSpecification> valueProvider =
+				entry.getAnnotationValueProvider(annotationClass);
 		IAnnotatedSpecification specification = entry.getSpecification(annotationClass);
-		EClassifier valueType = valueProvider.getEStructuralFeatureDescriptor().getEType(specification);
+		EClassifier valueType =
+				valueProvider.getEStructuralFeatureDescriptor().getEType(specification);
 
 		String columnName = valueProvider.getAnnotationName(specification);
 
@@ -361,51 +404,60 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 		TreeTableColumn<AnnotationEntry, String> column = null;
 
 		// Case distinction for the cell factory depending on the type to be edited.
-		if (valueType == null) {
+		if(valueType == null) {
 			column = annotationViewer.addColumn(columnName, 150);
 			column.setEditable(false);
 
 			return column;
-		} else if (valueType instanceof EEnum) {
+		} else if(valueType instanceof EEnum) {
 			// Add a combo column for enumerations.
 			column = annotationViewer.addComboColumn(columnName, 150, rowEntry -> {
-				ValueProviderBase<IAnnotatedSpecification> annotationValueProvider = (ValueProviderBase<IAnnotatedSpecification>) rowEntry
-						.getAnnotationValueProvider(annotationClass);
-				EStructuralFeatureDescriptor structuralFeature = annotationValueProvider
-						.getEStructuralFeatureDescriptor();
-				EClassifier type = structuralFeature.getEType(rowEntry.getSpecification(annotationClass));
+				ValueProviderBase<IAnnotatedSpecification> annotationValueProvider =
+						(ValueProviderBase<IAnnotatedSpecification>)rowEntry
+								.getAnnotationValueProvider(annotationClass);
+				EStructuralFeatureDescriptor structuralFeature =
+						annotationValueProvider.getEStructuralFeatureDescriptor();
+				EClassifier type =
+						structuralFeature.getEType(rowEntry.getSpecification(annotationClass));
 
 				// Get the enum literals again, as it might be different for each row.
-				if (type instanceof EEnum) {
-					EList<EEnumLiteral> allLiterals = ((EEnum) type).getELiterals();
+				if(type instanceof EEnum) {
+					EList<EEnumLiteral> allLiterals = ((EEnum)type).getELiterals();
 
 					return allLiterals.stream().map(l -> l.getLiteral()).collect(toList());
 				}
 				return emptyList();
 			});
-		} else if (valueType.getInstanceClass().equals(Boolean.class)) {
+		} else if(valueType.getInstanceClass().equals(Boolean.class)) {
 			// Use a checkbox column for booleans
 			annotationViewer.addCheckboxColumn(columnName, 100);
 		} else {
 			// For all other types, there is text editing.
 			column = annotationViewer.addColumn(columnName, 150);
 			column.setCellFactory(ttColumn -> new DynamicTextFieldTreeTableCell<AnnotationEntry>() {
+				/** {@inheritDoc} */
+				@Override
 				protected boolean canEdit(AnnotationEntry element) {
 					// We start editing only, if this cell is supposed to be editable.
-					IAnnotationValueProvider<IAnnotatedSpecification> annotationValueProvider = element
-							.getAnnotationValueProvider(annotationClass);
-					IAnnotatedSpecification currentSpecification = element.getSpecification(annotationClass);
-					return annotationValueProvider != null && annotationValueProvider.canEdit(currentSpecification);
+					IAnnotationValueProvider<IAnnotatedSpecification> annotationValueProvider =
+							element.getAnnotationValueProvider(annotationClass);
+					IAnnotatedSpecification currentSpecification =
+							element.getSpecification(annotationClass);
+					return annotationValueProvider != null &&
+							annotationValueProvider.canEdit(currentSpecification);
 				}
 
+				/** {@inheritDoc} */
+				@Override
 				protected void setValueOnLeavingCell(AnnotationEntry element, String currentValue) {
-					IAnnotatedSpecification specification = element.getSpecification(annotationClass);
+					IAnnotatedSpecification specification =
+							element.getSpecification(annotationClass);
 					setAnnotationEntryValue(specification, element, currentValue);
 				}
 			});
 		}
 
-		if (column != null) {
+		if(column != null) {
 			column.setEditable(true);
 
 			column.setOnEditCommit(event -> {
@@ -424,23 +476,25 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 	 * Local helper to set the given newValue for the given annotationEntry and
 	 * specification.
 	 */
-	private void setAnnotationEntryValue(IAnnotatedSpecification specification, AnnotationEntry annotationEntry,
-			String newValue) {
-		ITopLevelElement tle = IPersistencyService.getInstance().getTopLevelElementFor(curentRootElement);
+	private void setAnnotationEntryValue(IAnnotatedSpecification specification,
+			AnnotationEntry annotationEntry, String newValue) {
+		ITopLevelElement tle =
+				IPersistencyService.getInstance().getTopLevelElementFor(curentRootElement);
 
 		tle.runAsCommand(() -> {
 			try {
 				annotationEntry.setSpecificationValue(newValue, specification.getClass());
-			} catch (Exception e1) {
+			} catch(Exception e1) {
 				showWarning("Problem during setting an AnnotationValue: \n" + e1.getStackTrace());
 			}
 		});
 	}
 
-	private String getAnnotationEntryName(AnnotationEntry entry) {
+	/** Retrieves the model element name to be displayed for the given {@link AnnotationEntry}. */
+	private String getNameForAnnotationEntry(AnnotationEntry entry) {
 		IModelElement modelElement = entry.getModelElement();
-		if (modelElement instanceof INamedCommentedElement) {
-			return ((INamedCommentedElement) modelElement).getName();
+		if(modelElement instanceof INamedCommentedElement) {
+			return ((INamedCommentedElement)modelElement).getName();
 		}
 		return "";
 	}
@@ -448,78 +502,42 @@ public class AnnotationViewFXController extends CompositeFXControllerBase<SplitP
 	/**
 	 * UIProvider for the Annotation TreeTable.
 	 */
-	protected class AnnotationTreeTableUIProvider extends DynamicTreeTableUIProviderBase<AnnotationEntry> {
+	private class AnnotationTreeTableUIProvider
+			extends DynamicTreeTableUIProviderBase<AnnotationEntry> {
 
 		/** {@inheritDoc} */
 		@Override
 		public String getLabel(AnnotationEntry entry, int column) {
 			IModelElement element = entry.getModelElement();
 
-			switch (column) {
-			case 0:
-				return getAnnotationEntryName(entry);
-			case 1:
-				if (element instanceof INamedCommentedElement) {
-					return ((INamedCommentedElement) element).getComment();
-				}
-				return "";
-			default:
-				IAnnotatedSpecification columnSpec = colIdxAnnotationMap.get(column).getAnnotatedSpecification();
-
-				Object specificationValue = entry.getSpecificationValue(columnSpec.getClass());
-				if (specificationValue != null) {
-					return specificationValue.toString();
-				}
-
-				return "";
+			switch(column) {
+				case 0:
+					return getNameForAnnotationEntry(entry);
+				case 1:
+					if(element instanceof INamedCommentedElement) {
+						return ((INamedCommentedElement)element).getComment();
+					}
+					return "";
+				default:
+					IAnnotatedSpecification columnSpec =
+							colIdxAnnotationMap.get(column).getAnnotatedSpecification();
+
+					Object specificationValue = entry.getSpecificationValue(columnSpec.getClass());
+					if(specificationValue != null) {
+						return specificationValue.toString();
+					}
+
+					return "";
 			}
 		}
 	}
 
-	/**
-	 * Content provider, that can deal with the {@link ArtificialRoot}.
-	 * 
-	 * The tree under this root has only one layer.
-	 */
-	protected class AnnotationTableContentProvider extends DynamicTreeContentProviderBase<AnnotationEntry> {
-
-		/** Constructor */
-		public AnnotationTableContentProvider() {
-			// Initializes the filterExpressino to be non empty.
-			super.setFilterExpression("*");
-		}
-
-		/** {@inheritDoc} */
-		@Override
-		protected Collection<? extends AnnotationEntry> getChildren(AnnotationEntry parent) {
-			if (parent instanceof ArtificialRoot) {
-				return ((ArtificialRoot) parent).elements;
-			}
-
-			return emptyList();
-		}
-
-		/** {@inheritDoc} */
-		@Override
-		protected boolean filter(AnnotationEntry entry, String filterValue) {
-			return filter.passesRowFilter(entry);
-		}
-
-		/** {@inheritDoc} */
-		@Override
-		public void setFilterExpression(String filterExpression) {
-			throw new UnsupportedOperationException(
-					"The AnnotationTableContentProvider class does not use the build in filter expression. "
-							+ "This set operation would not have any effect.");
-		}
-	}
-
 	/**
 	 * Local class to represent the invisible root for the tree table.
 	 * 
 	 * The only thing relevant here, is the {@link List} 'elements'.
 	 */
-	private class ArtificialRoot extends AnnotationEntry {
+	protected class ArtificialRoot extends AnnotationEntry {
 
 		/**
 		 * Constructor.
diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/FXAnnotationFilter.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/FXAnnotationFilterContentProvider.java
similarity index 72%
rename from org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/FXAnnotationFilter.java
rename to org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/FXAnnotationFilterContentProvider.java
index 05bc78a53..6c92d9c96 100644
--- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/FXAnnotationFilter.java
+++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/view/fx/FXAnnotationFilterContentProvider.java
@@ -15,11 +15,13 @@
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.base.ui.annotation.view.fx;
 
+import static java.util.Collections.emptyList;
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
 import static java.util.regex.Pattern.DOTALL;
 import static org.fortiss.tooling.kernel.utils.EcoreUtils.getModelElementAncestor;
 import static org.fortiss.tooling.kernel.utils.EcoreUtils.getModelElementLevel;
 
+import java.util.Collection;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
 
@@ -27,21 +29,25 @@ import org.eclipse.emf.ecore.EObject;
 import org.fortiss.tooling.base.annotation.AnnotationEntry;
 import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
 import org.fortiss.tooling.base.model.element.IModelElement;
+import org.fortiss.tooling.base.ui.annotation.view.fx.AnnotationViewFXController.ArtificialRoot;
 import org.fortiss.tooling.base.ui.annotation.view.generic.ColumnHandle;
+import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeContentProviderBase;
 import org.fortiss.tooling.kernel.model.INamedElement;
 import org.fortiss.tooling.kernel.model.IProjectRootElement;
 
 /**
- * Row and column filter for the {@link AnnotationViewFXController}.
+ * Content provider with row and column filter for the {@link AnnotationViewFXController}.
  * <ul>
- * {@link FXAnnotationFilter#passesColumnFilter(ColumnHandle)} that is evaluated
- * during the update of the {@link AnnotationViewFXController}.</li>
+ * {@link FXAnnotationFilterContentProvider#passesColumnFilter(ColumnHandle)}
+ * that is evaluated during the update of the
+ * {@link AnnotationViewFXController}.</li>
  * </ul>
  * 
  * @author barner
  * @author bayha
  */
-public class FXAnnotationFilter {
+public class FXAnnotationFilterContentProvider
+		extends DynamicTreeContentProviderBase<AnnotationEntry> {
 	/** Filter hint text. */
 	static final String FILTER_HINT_TEXT = "type filter text (regex)";
 
@@ -83,6 +89,13 @@ public class FXAnnotationFilter {
 	/** Column filter option based on annotation type. */
 	private Class<? extends IAnnotatedSpecification> annotationTypeFilter;
 
+	/** Constructor */
+	public FXAnnotationFilterContentProvider() {
+		// Initialize filter expression to enable filtering also for empty patterns.
+		// (i.e. if only columns hall be filtered).
+		setFilterExpression("*");
+	}
+
 	/**
 	 * Returns {@code true} if a given {@code input} passes a case-insensitive
 	 * filter specified by {@code filterString}.
@@ -91,12 +104,12 @@ public class FXAnnotationFilter {
 		// Null-filter accepts every input. This covers also the case when the pattern
 		// provided by
 		// the user is invalid.
-		if (filterPattern == null) {
+		if(filterPattern == null) {
 			return true;
 		}
 
 		// Null-input cannot be accepted by a non-null filter
-		if (input == null) {
+		if(input == null) {
 			return false;
 		}
 
@@ -108,30 +121,31 @@ public class FXAnnotationFilter {
 	 * model element (or if this particular filter option is turned off).
 	 */
 	private boolean passesSelectedElementTypeFilter(AnnotationEntry annotationEntry) {
-		boolean selectedElementIsInvalid = currentlySelectedModelElement == null
-				|| currentlySelectedModelElement instanceof IProjectRootElement;
+		boolean selectedElementIsInvalid = currentlySelectedModelElement == null ||
+				currentlySelectedModelElement instanceof IProjectRootElement;
 
-		if (selectedElementIsInvalid || hierarchyLevelFilter == null) {
+		if(selectedElementIsInvalid || hierarchyLevelFilter == null) {
 			return true;
 		}
 
 		IModelElement modelElement = annotationEntry.getModelElement();
 		int modelElementLevel = getModelElementLevel(modelElement);
-		int currentlySelectedModelElementLevel = getModelElementLevel(currentlySelectedModelElement);
+		int currentlySelectedModelElementLevel =
+				getModelElementLevel(currentlySelectedModelElement);
 
-		if (hierarchyLevelFilter.equals(HIERARCHY_LEVELS_CURRENT)) {
+		if(hierarchyLevelFilter.equals(HIERARCHY_LEVELS_CURRENT)) {
 			// Elements have different levels -> filter
-			if (modelElementLevel != currentlySelectedModelElementLevel) {
+			if(modelElementLevel != currentlySelectedModelElementLevel) {
 				return false;
 			}
 
-			if (currentlySelectedModelElement.eContainer() != modelElement.eContainer()) {
+			if(currentlySelectedModelElement.eContainer() != modelElement.eContainer()) {
 				return false;
 			}
 		}
-		if (hierarchyLevelFilter.equals(HIERARCHY_LEVELS_SELECTED_SUBMODEL)) {
+		if(hierarchyLevelFilter.equals(HIERARCHY_LEVELS_SELECTED_SUBMODEL)) {
 			// Model element has lower level than currently selected element -> filter
-			if (modelElementLevel < currentlySelectedModelElementLevel) {
+			if(modelElementLevel < currentlySelectedModelElementLevel) {
 				return false;
 			}
 
@@ -141,20 +155,23 @@ public class FXAnnotationFilter {
 			// identical
 			// to the currently selected model element.
 			int levelsUp = modelElementLevel - currentlySelectedModelElementLevel;
-			EObject modelElementAncestor = getModelElementAncestor(annotationEntry.getModelElement(), levelsUp);
+			EObject modelElementAncestor =
+					getModelElementAncestor(annotationEntry.getModelElement(), levelsUp);
 
-			if (currentlySelectedModelElement != modelElementAncestor) {
+			if(currentlySelectedModelElement != modelElementAncestor) {
 				return false;
 			}
 		}
 
 		Class<? extends IModelElement> modelElementClass = modelElement.getClass();
-		Class<? extends IModelElement> currentlySelectedModelElementClass = currentlySelectedModelElement.getClass();
+		Class<? extends IModelElement> currentlySelectedModelElementClass =
+				currentlySelectedModelElement.getClass();
 
 		// Pass test if either the model element type is not restricted, or the element
 		// has the same
 		// type as the selected one
-		return !restrictToSelectedModelElementType || modelElementClass.equals(currentlySelectedModelElementClass);
+		return !restrictToSelectedModelElementType ||
+				modelElementClass.equals(currentlySelectedModelElementClass);
 	}
 
 	/**
@@ -176,7 +193,8 @@ public class FXAnnotationFilter {
 				.isHidden(columnHandle.getAnnotatedSpecification().getClass());
 
 		// Pass name filter, if: name is not filtered, or the condition is met
-		boolean passesNameFilter = !filterColumnName || passesNameFilter(columnHandle.getColumnName());
+		boolean passesNameFilter =
+				!filterColumnName || passesNameFilter(columnHandle.getColumnName());
 
 		// Check if column passes all tests
 		// @CodeFormatterOff
@@ -195,21 +213,21 @@ public class FXAnnotationFilter {
 		// Pass name filter, if: name is not filtered, element is not a INamedElement,
 		// or it
 		// actually passes the filter
-		boolean passesNameFilter = !filterRowName || !(modelElement instanceof INamedElement)
-				|| passesNameFilter(((INamedElement) modelElement).getName());
+		boolean passesNameFilter = !filterRowName || !(modelElement instanceof INamedElement) ||
+				passesNameFilter(((INamedElement)modelElement).getName());
 
 		return passesNameFilter && passesSelectedElementTypeFilter(annotationEntry);
 	}
 
 	/** Sets the model element / annotation name filter pattern. */
-	public void setNameFilterPattern(String filterPatternString) {
-		if (filterPatternString.equals(FILTER_HINT_TEXT)) {
+	private void setNameFilterPattern(String filterPatternString) {
+		if(filterPatternString.equals(FILTER_HINT_TEXT)) {
 			filterPattern = null;
 		} else {
 			try {
 				final int patternFlags = (filterNameMatchCase ? 0 : CASE_INSENSITIVE) | DOTALL;
 				filterPattern = Pattern.compile(".*" + filterPatternString + ".*", patternFlags);
-			} catch (PatternSyntaxException e) {
+			} catch(PatternSyntaxException e) {
 				filterPattern = null;
 			}
 		}
@@ -229,7 +247,7 @@ public class FXAnnotationFilter {
 	public void setFilterNameMatchCase(boolean filterNameMatchCase) {
 		this.filterNameMatchCase = filterNameMatchCase;
 		// Recompile pattern
-		if (filterPattern != null) {
+		if(filterPattern != null) {
 			setNameFilterPattern(filterPattern.pattern());
 		}
 	}
@@ -248,7 +266,8 @@ public class FXAnnotationFilter {
 	}
 
 	/** Sets the annotation type filter option. */
-	public void setAnnotationTypeFilter(Class<? extends IAnnotatedSpecification> annotationTypeFilter) {
+	public void
+			setAnnotationTypeFilter(Class<? extends IAnnotatedSpecification> annotationTypeFilter) {
 		this.annotationTypeFilter = annotationTypeFilter;
 	}
 
@@ -256,4 +275,34 @@ public class FXAnnotationFilter {
 	public void setCurrentySelectedModelElement(IModelElement currentySelectedModelElement) {
 		this.currentlySelectedModelElement = currentySelectedModelElement;
 	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected Collection<? extends AnnotationEntry> getChildren(AnnotationEntry parent) {
+		if(parent instanceof ArtificialRoot) {
+			return ((ArtificialRoot)parent).elements;
+		}
+
+		return emptyList();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	protected boolean filter(AnnotationEntry entry, String filterValue) {
+		return passesRowFilter(entry);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void setFilterExpression(String filterExpression) {
+		// An empty expression would permit the content provider from calling the filter
+		// method. Hence it an empty expression is replaced by "*".
+		if(filterExpression == null || filterExpression.isEmpty()) {
+			setNameFilterPattern("*");
+			super.setFilterExpression("*");
+		} else {
+			setNameFilterPattern(filterExpression);
+			super.setFilterExpression(filterExpression);
+		}
+	}
 }
-- 
GitLab