Bug 378495 - Part Toolbar does not refresh when toolbar items are
removed

- Update the ToolBarManager when MToolBar children change: add,
remove, move.
- Reduce the number or add/remove/move events when a ToolBarManager is
reconciled to model
- Reconcile  visibility of existing items from ToolBarManager to model

Change-Id: I21ee015b1cdbf226ac5eba6d729de5365b70cc32
Signed-off-by: Rolf Theunissen <rolf.theunissen@gmail.com>
diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRenderer.java b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRenderer.java
index ee45906..835ed19 100644
--- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRenderer.java
+++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRenderer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2018 IBM Corporation and others.
+ * Copyright (c) 2009, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,7 @@
  *     Patrik Suzzi <psuzzi@gmail.com> - Bug 473184
  *     Simon Scholz <simon.scholz@vogella.com> - Bug 506306
  *     Axel Richard <axel.richard@oebo.fr> - Bug 354538
+ *     Rolf Theunissen <rolf.theunissen@gmail.com> - Bug 378495
  *******************************************************************************/
 package org.eclipse.e4.ui.workbench.renderers.swt;
 
@@ -69,6 +70,8 @@
 import org.eclipse.e4.ui.workbench.UIEvents.ElementContainer;
 import org.eclipse.e4.ui.workbench.UIEvents.EventTags;
 import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.emf.common.util.ECollections;
+import org.eclipse.emf.common.util.EList;
 import org.eclipse.jface.action.AbstractGroupMarker;
 import org.eclipse.jface.action.ContributionItem;
 import org.eclipse.jface.action.GroupMarker;
@@ -175,19 +178,12 @@
 			if (itemModel.isToBeRendered()) {
 				if (parent != null) {
 					modelProcessSwitch(parent, itemModel);
-					parent.update(true);
-					ToolBar toolbar = parent.getControl();
-					if (toolbar != null && !toolbar.isDisposed()) {
-						toolbar.requestLayout();
-					}
+					updateWidget(parent);
 				}
 			} else {
-				IContributionItem ici = modelToContribution.remove(itemModel);
-				if (ici != null && parent != null) {
-					parent.remove(ici);
-				}
-				if (ici != null) {
-					ici.dispose();
+				removeElement(parent, itemModel);
+				if (parent != null) {
+					updateWidget(parent);
 				}
 			}
 		} else if (UIEvents.UIElement.VISIBLE.equals(attName)) {
@@ -222,12 +218,7 @@
 				}
 			}
 
-			parent.markDirty();
-			parent.update(true);
-			ToolBar toolbar = parent.getControl();
-			if (toolbar != null && !toolbar.isDisposed()) {
-				toolbar.requestLayout();
-			}
+			updateWidget(parent);
 		}
 	}
 
@@ -261,18 +252,42 @@
 		}
 	}
 
-	@SuppressWarnings("unchecked")
 	@Inject
 	@Optional
-	private void subscribeTopicChildAdded(@UIEventTopic(ElementContainer.TOPIC_CHILDREN) Event event) {
+	private void subscribeTopicUpdateChildren(@UIEventTopic(ElementContainer.TOPIC_CHILDREN) Event event) {
 		// Ensure that this event is for a MToolBar
 		if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MToolBar)) {
 			return;
 		}
+
 		MToolBar toolbarModel = (MToolBar) event.getProperty(UIEvents.EventTags.ELEMENT);
+		ToolBarManager parentManager = getManager(toolbarModel);
+		if (parentManager == null) {
+			return;
+		}
+
 		if (UIEvents.isADD(event)) {
-			Object obj = toolbarModel;
-			processContents((MElementContainer<MUIElement>) obj);
+			for (Object o : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) {
+				MToolBarElement added = (MToolBarElement) o;
+				modelProcessSwitch(parentManager, added);
+			}
+			updateWidget(parentManager);
+		} else if (UIEvents.isREMOVE(event)) {
+			for (Object o : UIEvents.asIterable(event, UIEvents.EventTags.OLD_VALUE)) {
+				MToolBarElement removed = (MToolBarElement) o;
+				removed.setRenderer(null);
+				removeElement(parentManager, removed);
+			}
+			updateWidget(parentManager);
+		} else if (UIEvents.isMOVE(event)) {
+			MToolBarElement moved = (MToolBarElement) event.getProperty(UIEvents.EventTags.NEW_VALUE);
+			Integer newPos = (Integer) event.getProperty(UIEvents.EventTags.POSITION);
+
+			IContributionItem ici = getContribution(moved);
+			parentManager.remove(ici);
+			parentManager.insert(newPos, ici);
+
+			updateWidget(parentManager);
 		}
 	}
 
@@ -624,30 +639,16 @@
 				modelProcessSwitch(parentManager, (MToolBarElement) childME);
 			}
 		}
-		parentManager.update(true);
 
-		ToolBar toolbar = getToolbarFrom(container.getWidget());
-		if (toolbar != null) {
-			toolbar.requestLayout();
-		}
+		updateWidget(parentManager);
 	}
 
-	private ToolBar getToolbarFrom(Object widget) {
-		if (widget instanceof ToolBar) {
-			return (ToolBar) widget;
+	private void updateWidget(ToolBarManager manager) {
+		manager.update(true);
+		ToolBar toolbar = manager.getControl();
+		if (toolbar != null && !toolbar.isDisposed()) {
+			toolbar.requestLayout();
 		}
-		if (widget instanceof Composite) {
-			Composite intermediate = (Composite) widget;
-			if (!intermediate.isDisposed()) {
-				Control[] children = intermediate.getChildren();
-				for (Control control : children) {
-					if (control.getData() instanceof ToolBarManager) {
-						return (ToolBar) control;
-					}
-				}
-			}
-		}
-		return null;
 	}
 
 	boolean hasOnlySeparators(ToolBar toolbar) {
@@ -835,6 +836,19 @@
 		}
 	}
 
+	private void removeElement(ToolBarManager parentManager, MToolBarElement toolBarElement) {
+		IContributionItem ici = getContribution(toolBarElement);
+		clearModelToContribution(toolBarElement, ici);
+		if (ici != null && parentManager != null) {
+			// Check if the ici is (still) in the manager; e.g. while reconciling
+			// Whoever removes an ici form the manager is responsible for disposal
+			ici = parentManager.remove(ici);
+		}
+		if (ici != null) {
+			ici.dispose();
+		}
+	}
+
 	/**
 	 * @param model
 	 * @return mapped manager, if any
@@ -973,46 +987,23 @@
 	 * @param toolBar
 	 */
 	public void reconcileManagerToModel(IToolBarManager menuManager, MToolBar toolBar) {
-		List<MToolBarElement> modelChildren = toolBar.getChildren();
-		HashSet<MToolItem> oldModelItems = new HashSet<>();
-		for (MToolBarElement itemModel : modelChildren) {
-			if (OpaqueElementUtil.isOpaqueToolItem(itemModel)) {
-				oldModelItems.add((MToolItem) itemModel);
-			}
-		}
+		List<MToolBarElement> newChildren = new ArrayList<>();
 
 		IContributionItem[] items = menuManager.getItems();
-		for (int src = 0, dest = 0; src < items.length; src++, dest++) {
-			IContributionItem item = items[src];
+		for (IContributionItem item : items) {
 			MToolBarElement element = getToolElement(item);
 			if (element == null) {
-				MToolItem legacyItem = OpaqueElementUtil.createOpaqueToolItem();
-				legacyItem.setElementId(item.getId());
-				legacyItem.setVisible(item.isVisible());
-				OpaqueElementUtil.setOpaqueItem(legacyItem, item);
-				linkModelToContribution(legacyItem, item);
-				modelChildren.add(dest, legacyItem);
-			} else if (OpaqueElementUtil.isOpaqueToolItem(element)) {
-				MToolItem legacyItem = (MToolItem) element;
-				oldModelItems.remove(legacyItem);
-				if (modelChildren.size() > dest) {
-					if (modelChildren.get(dest) != legacyItem) {
-						modelChildren.remove(legacyItem);
-						modelChildren.add(dest, legacyItem);
-					}
-				} else {
-					modelChildren.add(legacyItem);
-				}
+				element = OpaqueElementUtil.createOpaqueToolItem();
+				element.setElementId(item.getId());
+				OpaqueElementUtil.setOpaqueItem(element, item);
+				element.setRenderer(this);
+				linkModelToContribution(element, item);
 			}
+			element.setVisible(item.isVisible());
+			newChildren.add(element);
 		}
 
-		if (!oldModelItems.isEmpty()) {
-			modelChildren.removeAll(oldModelItems);
-			for (MToolItem model : oldModelItems) {
-				Object obj = OpaqueElementUtil.getOpaqueItem(model);
-				clearModelToContribution(model, (IContributionItem) obj);
-			}
-		}
+		ECollections.setEList((EList<MToolBarElement>) toolBar.getChildren(), newChildren);
 	}
 
 	@Override
diff --git a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java
index ce8905c..443338d 100644
--- a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java
+++ b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java
@@ -49,6 +49,7 @@
 import org.eclipse.e4.ui.workbench.renderers.swt.StackRendererTest;
 import org.eclipse.e4.ui.workbench.renderers.swt.TabStateHandlerTest;
 import org.eclipse.e4.ui.workbench.renderers.swt.ThemeDefinitionChangedHandlerTest;
+import org.eclipse.e4.ui.workbench.renderers.swt.ToolBarManagerRendererTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
@@ -87,6 +88,7 @@
 		StackRendererTest.class,
 		TabStateHandlerTest.class,
 		ThemeDefinitionChangedHandlerTest.class,
+		ToolBarManagerRendererTest.class,
 		TopoSortTests.class,
 		ExtensionsSortTests.class,
 		HandlerActivationTest.class,
diff --git a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRendererTest.java b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRendererTest.java
new file mode 100644
index 0000000..a91229c
--- /dev/null
+++ b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolBarManagerRendererTest.java
@@ -0,0 +1,419 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Rolf Theunissen and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Rolf Theunissen - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.e4.ui.workbench.renderers.swt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import javax.inject.Inject;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar;
+import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
+import org.eclipse.e4.ui.model.application.ui.menu.MDirectToolItem;
+import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
+import org.eclipse.e4.ui.model.application.ui.menu.MToolItem;
+import org.eclipse.e4.ui.tests.rules.WorkbenchContextRule;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.emf.common.util.ECollections;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.ToolBarManager;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ToolBarManagerRendererTest {
+
+	@Rule
+	public WorkbenchContextRule contextRule = new WorkbenchContextRule();
+
+	@Inject
+	private EModelService ems;
+
+	@Inject
+	private MApplication application;
+
+	private MToolBar toolBar;
+	private MTrimmedWindow window;
+
+	@Before
+	public void setUp() throws Exception {
+		window = ems.createModelElement(MTrimmedWindow.class);
+		application.getChildren().add(window);
+
+		MTrimBar trimBar = ems.createModelElement(MTrimBar.class);
+		window.getTrimBars().add(trimBar);
+
+		toolBar = ems.createModelElement(MToolBar.class);
+		trimBar.getChildren().add(toolBar);
+	}
+
+	@Test
+	public void testDynamicItem_AddOne() {
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(0, tbm.getSize());
+
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem1);
+
+		assertEquals(1, tbm.getSize());
+	}
+
+	@Test
+	public void testDynamicItem_AddOneBefore() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem1);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(1, tbm.getSize());
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolItem2.setElementId("Item2");
+		toolBar.getChildren().add(0, toolItem2);
+
+		assertEquals(2, tbm.getSize());
+		assertEquals("Item2", tbm.getItems()[0].getId());
+	}
+
+	@Test
+	public void testDynamicItem_AddMany() {
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(0, tbm.getSize());
+
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+
+		List<MToolItem> itemList = Arrays.asList(toolItem1, toolItem2);
+		toolBar.getChildren().addAll(itemList);
+
+		assertEquals(2, tbm.getSize());
+	}
+
+	@Test
+	public void testDynamicItem_RemoveOne() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem1);
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolItem2.setElementId("Item2");
+		toolBar.getChildren().add(toolItem2);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(2, tbm.getSize());
+		assertNotNull(toolItem1.getWidget());
+
+		toolBar.getChildren().remove(0);
+
+		assertEquals(1, tbm.getSize());
+		assertEquals("Item2", tbm.getItems()[0].getId());
+
+		// Ensure that the removed item is disposed
+		assertNull(toolItem1.getWidget());
+	}
+
+	@Test
+	public void testDynamicItem_RemoveMany() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem1);
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolItem2.setElementId("Item2");
+		toolBar.getChildren().add(toolItem2);
+
+		MToolItem toolItem3 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem3);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(3, tbm.getSize());
+
+		List<MToolItem> itemList = Arrays.asList(toolItem1, toolItem3);
+		toolBar.getChildren().removeAll(itemList);
+
+		assertEquals(1, tbm.getSize());
+		assertEquals("Item2", tbm.getItems()[0].getId());
+	}
+
+	@Test
+	public void testDynamicItem_RemoveAll() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem1);
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem2);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(2, tbm.getSize());
+
+		toolBar.getChildren().clear();
+
+		assertEquals(0, tbm.getSize());
+	}
+
+	@Test
+	public void testDynamicItem_Move() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolItem1.setElementId("Item1");
+		toolBar.getChildren().add(toolItem1);
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolItem2.setElementId("Item2");
+		toolBar.getChildren().add(toolItem2);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(2, tbm.getSize());
+		assertEquals("Item1", tbm.getItems()[0].getId());
+		assertEquals("Item2", tbm.getItems()[1].getId());
+
+		ECollections.move(toolBar.getChildren(), 0, 1);
+
+		assertEquals(2, tbm.getSize());
+		assertEquals("Item2", tbm.getItems()[0].getId());
+		assertEquals("Item1", tbm.getItems()[1].getId());
+	}
+
+	@Test
+	public void testDynamicItem_Visibility() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolBar.getChildren().add(toolItem1);
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolItem2.setVisible(false);
+		toolBar.getChildren().add(toolItem2);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(2, tbm.getSize());
+		assertTrue(tbm.getItems()[0].isVisible());
+		assertFalse(tbm.getItems()[1].isVisible());
+
+		toolItem1.setVisible(false);
+
+		assertEquals(2, tbm.getSize());
+		assertFalse(tbm.getItems()[0].isVisible());
+		assertFalse(tbm.getItems()[1].isVisible());
+
+		toolItem1.setVisible(true);
+
+		assertEquals(2, tbm.getSize());
+		assertTrue(tbm.getItems()[0].isVisible());
+		assertFalse(tbm.getItems()[1].isVisible());
+	}
+
+	@Test
+	public void testDynamicItem_Reconcile_AddOne() {
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManagerRenderer renderer = getToolBarManagerRenderer();
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(0, toolBar.getChildren().size());
+		assertEquals(0, tbm.getSize());
+
+		TestActionContributionItem item1 = new TestActionContributionItem();
+		item1.setId("Item1");
+		tbm.add(item1);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(1, toolBar.getChildren().size());
+		assertEquals("Item1", toolBar.getChildren().get(0).getElementId());
+		assertEquals(1, tbm.getSize());
+		assertEquals(item1, tbm.getItems()[0]);
+	}
+
+	@Test
+	public void testDynamicItem_Reconcile_RemoveOne() {
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManagerRenderer renderer = getToolBarManagerRenderer();
+		ToolBarManager tbm = getToolBarManager();
+
+		TestActionContributionItem item1 = new TestActionContributionItem();
+		tbm.add(item1);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(1, toolBar.getChildren().size());
+		assertEquals(1, tbm.getSize());
+		assertFalse(item1.disposed);
+
+		tbm.remove(item1);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(0, toolBar.getChildren().size());
+		assertEquals(0, tbm.getSize());
+
+		assertFalse(item1.disposed);
+		item1.dispose();
+		assertTrue(item1.disposed);
+	}
+
+	@Test
+	public void testDynamicItem_Reconcile_RemoveOneByID() {
+		MToolItem toolItem1 = ems.createModelElement(MDirectToolItem.class);
+		toolItem1.setElementId("Item1");
+		toolBar.getChildren().add(toolItem1);
+
+		MToolItem toolItem2 = ems.createModelElement(MDirectToolItem.class);
+		toolItem2.setElementId("Item2");
+		toolBar.getChildren().add(toolItem2);
+
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManagerRenderer renderer = getToolBarManagerRenderer();
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(2, toolBar.getChildren().size());
+		assertEquals(2, tbm.getSize());
+
+		tbm.remove("Item1");
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(1, toolBar.getChildren().size());
+		assertEquals(1, tbm.getSize());
+		assertEquals("Item2", tbm.getItems()[0].getId());
+	}
+
+	@Test
+	public void testDynamicItem_Reconcile_Move() {
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManagerRenderer renderer = getToolBarManagerRenderer();
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(0, toolBar.getChildren().size());
+		assertEquals(0, tbm.getSize());
+
+		TestActionContributionItem item1 = new TestActionContributionItem();
+		item1.setId("Item1");
+		tbm.add(item1);
+		TestActionContributionItem item2 = new TestActionContributionItem();
+		item2.setId("Item2");
+		tbm.add(item2);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(2, toolBar.getChildren().size());
+		assertEquals("Item1", toolBar.getChildren().get(0).getElementId());
+		assertEquals("Item2", toolBar.getChildren().get(1).getElementId());
+
+		assertEquals(2, tbm.getSize());
+		assertEquals(item1, tbm.getItems()[0]);
+		assertEquals(item2, tbm.getItems()[1]);
+		assertFalse(item1.disposed);
+		assertFalse(item2.disposed);
+
+		tbm.remove(item1);
+		tbm.remove(item2);
+		tbm.add(item2);
+		tbm.add(item1);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(2, toolBar.getChildren().size());
+		assertEquals("Item2", toolBar.getChildren().get(0).getElementId());
+		assertEquals("Item1", toolBar.getChildren().get(1).getElementId());
+
+		assertEquals(2, tbm.getSize());
+		assertEquals(item2, tbm.getItems()[0]);
+		assertEquals(item1, tbm.getItems()[1]);
+		assertFalse(item1.disposed);
+		assertFalse(item2.disposed);
+	}
+
+	@Test
+	public void testDynamicItem_Reconcile_Visibility() {
+		contextRule.createAndRunWorkbench(window);
+		ToolBarManagerRenderer renderer = getToolBarManagerRenderer();
+		ToolBarManager tbm = getToolBarManager();
+
+		assertEquals(0, toolBar.getChildren().size());
+		assertEquals(0, tbm.getSize());
+
+		TestActionContributionItem item1 = new TestActionContributionItem();
+		tbm.add(item1);
+		TestActionContributionItem item2 = new TestActionContributionItem();
+		tbm.add(item2);
+		item2.setVisible(false);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(2, toolBar.getChildren().size());
+		assertTrue(toolBar.getChildren().get(0).isVisible());
+		assertFalse(toolBar.getChildren().get(1).isVisible());
+		assertTrue(item1.isVisible());
+		assertFalse(item2.isVisible());
+
+		item1.setVisible(false);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(2, toolBar.getChildren().size());
+		assertFalse(toolBar.getChildren().get(0).isVisible());
+		assertFalse(toolBar.getChildren().get(1).isVisible());
+		assertFalse(item1.isVisible());
+		assertFalse(item2.isVisible());
+
+		item1.setVisible(true);
+		renderer.reconcileManagerToModel(tbm, toolBar);
+
+		assertEquals(2, toolBar.getChildren().size());
+		assertTrue(toolBar.getChildren().get(0).isVisible());
+		assertFalse(toolBar.getChildren().get(1).isVisible());
+		assertTrue(item1.isVisible());
+		assertFalse(item2.isVisible());
+	}
+
+	private ToolBarManagerRenderer getToolBarManagerRenderer() {
+		Object renderer = toolBar.getRenderer();
+		assertEquals(ToolBarManagerRenderer.class, renderer.getClass());
+		return (ToolBarManagerRenderer) renderer;
+	}
+
+	private ToolBarManager getToolBarManager() {
+		return (getToolBarManagerRenderer()).getManager(toolBar);
+	}
+
+
+	static private class TestActionContributionItem extends ActionContributionItem {
+		private boolean disposed = false;
+
+		public TestActionContributionItem() {
+			super(new Action("Dummy") {
+			});
+		}
+
+		@Override
+		public void dispose() {
+			disposed = true;
+			super.dispose();
+		}
+
+	}
+
+}