/*******************************************************************************
 * Copyright (c) 2004, 2010 Tasktop Technologies and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Tasktop Technologies - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.java.tests;

import java.io.File;
import java.io.IOException;
import java.util.Collections;

import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModel;
import org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.mylyn.context.core.AbstractContextListener;
import org.eclipse.mylyn.context.core.AbstractContextStructureBridge;
import org.eclipse.mylyn.context.core.ContextChangeEvent;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.context.core.IInteractionElement;
import org.eclipse.mylyn.internal.context.core.CompositeInteractionContext;
import org.eclipse.mylyn.internal.context.core.ContextCorePlugin;
import org.eclipse.mylyn.internal.context.core.InteractionContext;
import org.eclipse.mylyn.internal.context.core.InteractionContextScaling;
import org.eclipse.mylyn.internal.context.core.LocalContextStore;
import org.eclipse.mylyn.internal.java.ui.JavaStructureBridge;
import org.eclipse.mylyn.internal.resources.ui.ResourceStructureBridge;
import org.eclipse.mylyn.monitor.core.InteractionEvent;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;

/**
 * @author Mik Kersten
 */
public class InteractionContextManagerTest extends AbstractJavaContextTest {

	private PackageExplorerPart explorer;

	private LocalContextStore contextStore;

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		explorer = PackageExplorerPart.openInActivePerspective();
		contextStore = ContextCorePlugin.getContextStore();
		assertNotNull(explorer);
	}

	@Override
	protected void tearDown() throws Exception {
		super.tearDown();
	};

	class LandmarksModelListener extends AbstractContextListener {

		public int numAdditions = 0;

		public int numDeletions = 0;

		@Override
		public void contextChanged(ContextChangeEvent event) {
			switch (event.getEventKind()) {
			case LANDMARKS_ADDED:
				numAdditions += event.getElements().size();
				break;
			case LANDMARKS_REMOVED:
				numDeletions += event.getElements().size();
				break;
			}
		}
	}

	public void testHandleToPathConversion() throws IOException {
		String handle = "https://bugs.eclipse.org/bugs-123";
		File file = contextStore.getFileForContext(handle);
		assertFalse(file.exists());
		file.createNewFile();
		assertTrue(file.exists());
	}

	public void testPauseAndResume() throws JavaModelException {
		ContextCore.getContextManager().setContextCapturePaused(true);
		ContextCore.getContextManager().processInteractionEvent(mockInterestContribution("paused", 3));
		IInteractionElement paused = ContextCore.getContextManager().getElement("paused");
		assertFalse(paused.getInterest().isInteresting());

		ContextCore.getContextManager().setContextCapturePaused(false);
		ContextCore.getContextManager().processInteractionEvent(mockInterestContribution("paused", 3));
		IInteractionElement resumed = ContextCore.getContextManager().getElement("paused");
		assertTrue(resumed.getInterest().isInteresting());
	}

	// XXX 3.5 re-enable test
// NOTE: This is to test that the shell activation event is first in the activation history.
//		 Currently this test fails but passes when run with CoreUtil.TEST_MODE = true
//	public void testShellLifecycleActivityStart() {
//		List<InteractionEvent> events = manager.getActivityMetaContext().getInteractionHistory();
//		assertEquals("Activity monitoring possibly activated before context ui startup",
//				InteractionContextManager.ACTIVITY_DELTA_STARTED, events.get(0).getDelta());
//		assertEquals(InteractionContextManager.ACTIVITY_DELTA_ACTIVATED, events.get(1).getDelta());
//	}

	public void testActivityHistory() {
		manager.resetActivityMetaContext();
		InteractionContext history = manager.getActivityMetaContext();
		assertNotNull(history);
		assertEquals(0, manager.getActivityMetaContext().getInteractionHistory().size());

		manager.internalActivateContext(contextStore.loadContext("1"));
		assertEquals(1, manager.getActivityMetaContext().getInteractionHistory().size());

		manager.deactivateContext("2");
		assertEquals(2, manager.getActivityMetaContext().getInteractionHistory().size());
	}

	public void testChangeHandle() {
		ContextCore.getContextManager().processInteractionEvent(mockInterestContribution("old", 3));
		IInteractionElement old = ContextCore.getContextManager().getElement("old");
		assertTrue(old.getInterest().isInteresting());

		ContextCore.getContextManager().getActiveContext().updateElementHandle(old, "new");
		IInteractionElement changed = ContextCore.getContextManager().getElement("new");
		assertTrue(changed.getInterest().isInteresting());
	}

	public void testCopyContext() {
		File sourceFile = contextStore.getFileForContext(context.getHandleIdentifier());
		context.parseEvent(mockSelection("1"));
		assertFalse(context.getInteractionHistory().isEmpty());
		contextStore.saveContext(context);
		assertTrue(sourceFile.exists());

		File toFile = contextStore.getFileForContext("toContext");
		assertFalse(toFile.exists());

		contextStore.cloneContext(context.getHandleIdentifier(), "toContext");
		assertTrue(toFile.exists());

		manager.activateContext("toContext");
		IInteractionContext toContext = manager.getActiveContext();
		assertFalse(toContext.getInteractionHistory().isEmpty());
//		assertEquals(((CompositeInteractionContext) manager.getActiveContext()).get("toContext").getHandleIdentifier(),
//				toContext.getHandleIdentifier());

		toFile.delete();
		assertFalse(toFile.delete());
		manager.deactivateAllContexts();
	}

	public void testHasContext() {
		manager.deleteContext("1");
		assertFalse(contextStore.getFileForContext("1").exists());
		assertFalse(manager.hasContext("1"));
		manager.internalActivateContext(contextStore.loadContext("1"));
		assertTrue(manager.isContextActive());

		manager.deactivateContext("1");
		assertFalse(manager.hasContext("1"));

		manager.internalActivateContext(contextStore.loadContext("1"));
		manager.processInteractionEvent(mockSelection());
		manager.deactivateContext("1");
		assertTrue(manager.hasContext("1"));
		contextStore.getFileForContext("1").delete();
	}

	public void testDelete() {
		manager.deleteContext("1");
		assertFalse(contextStore.getFileForContext("1").exists());
		assertFalse(manager.hasContext("1"));
		manager.internalActivateContext(contextStore.loadContext("1"));
		assertTrue(manager.isContextActive());

		InteractionContext activeContext = ((CompositeInteractionContext) manager.getActiveContext()).getContextMap()
				.values()
				.iterator()
				.next();
		activeContext.parseEvent(mockSelection());
		assertTrue(containsHandle(activeContext, MOCK_HANDLE));
		activeContext.delete(activeContext.get(MOCK_HANDLE));
		assertFalse(containsHandle(activeContext, MOCK_HANDLE));

		manager.deactivateContext("1");
		assertFalse(manager.hasContext("1"));

		manager.activateContext("1");
		activeContext = ((CompositeInteractionContext) manager.getActiveContext()).getContextMap()
				.values()
				.iterator()
				.next();
		assertFalse(containsHandle(activeContext, MOCK_HANDLE));

		manager.internalActivateContext(contextStore.loadContext("1"));
		manager.processInteractionEvent(mockSelection());
		manager.deactivateContext("1");
		assertTrue(manager.hasContext("1"));
		contextStore.getFileForContext("1").delete();
	}

	private boolean containsHandle(InteractionContext context, String mockHandle) {
		for (IInteractionElement element : context.getAllElements()) {
			if (element.getHandleIdentifier().equals(mockHandle)) {
				return true;
			}
		}

		for (InteractionEvent element : context.getInteractionHistory()) {
			if (element.getStructureHandle().equals(mockHandle)) {
				return true;
			}
		}
		return false;
	}

	public void testPredictedInterest() {
		IInteractionElement node = ContextCore.getContextManager().getElement("doesn't exist");
		assertFalse(node.getInterest().isInteresting());
		assertFalse(node.getInterest().isPropagated());
	}

	public void testParentInterestAfterDecay() throws JavaModelException {
		IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart();
		IMethod m1 = type1.createMethod("void m1() { }", null, true, null);
		StructuredSelection sm1 = new StructuredSelection(m1);
		monitor.selectionChanged(part, sm1);

		IInteractionElement node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertTrue(node.getInterest().isInteresting());
		AbstractContextStructureBridge bridge = ContextCorePlugin.getDefault()
				.getStructureBridge(node.getContentType());
		IInteractionElement parent = ContextCore.getContextManager().getElement(
				bridge.getParentHandle(node.getHandleIdentifier()));
		assertTrue(parent.getInterest().isInteresting());
		assertTrue(parent.getInterest().isPropagated());

		for (int i = 0; i < 1 / (scaling.getDecay()) * 3; i++) {
			ContextCore.getContextManager().processInteractionEvent(mockSelection());
		}

		assertFalse(ContextCore.getContextManager().getElement(m1.getHandleIdentifier()).getInterest().isInteresting());
		ContextCore.getContextManager().processInteractionEvent(mockSelection(m1.getHandleIdentifier()));
		assertTrue(ContextCore.getContextManager().getElement(m1.getHandleIdentifier()).getInterest().isInteresting());
	}

	public void testPropagation() throws JavaModelException, Exception {
		IMethod m1 = type1.createMethod("void m1() { }", null, true, null);
		IInteractionElement node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertFalse(node.getInterest().isInteresting());

		InteractionEvent event = new InteractionEvent(InteractionEvent.Kind.MANIPULATION,
				new JavaStructureBridge().getContentType(), m1.getHandleIdentifier(), "source");
		ContextCorePlugin.getContextManager().processInteractionEvent(event, true);

		node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertTrue(node.getInterest().isInteresting());

		project.build();
		IJavaElement parent = m1.getParent();
		IInteractionElement parentNode = ContextCore.getContextManager().getElement(parent.getHandleIdentifier());
		assertFalse(parentNode.getInterest().isInteresting());

		InteractionEvent selectionEvent = new InteractionEvent(InteractionEvent.Kind.SELECTION,
				new JavaStructureBridge().getContentType(), m1.getHandleIdentifier(), "source");
		ContextCorePlugin.getContextManager().processInteractionEvent(selectionEvent, true);
		parentNode = ContextCore.getContextManager().getElement(parent.getHandleIdentifier());
		assertTrue(parentNode.getInterest().isInteresting());
	}

	public void testPropagationBetweenResourcesAndJava() throws JavaModelException, Exception {
		Workspace workspace = ((Workspace) ResourcesPlugin.getWorkspace());
		IPath fullPath = p1.getResource().getFullPath();

		IFolder newResource = (IFolder) workspace.newResource(fullPath.append("meta-inf"), IResource.FOLDER);
		newResource.create(true, true, new NullProgressMonitor());
		fullPath = newResource.getFullPath();

		IFile file = (IFile) workspace.newResource(fullPath.append("test.xml"), IResource.FILE);
		file.create(null, true, new NullProgressMonitor());

		ResourceStructureBridge resourceStructureBridge = new ResourceStructureBridge();
		String fileHandle = resourceStructureBridge.getHandleIdentifier(file);
		IInteractionElement node = ContextCore.getContextManager().getElement(fileHandle);
		assertFalse(node.getInterest().isInteresting());

		InteractionEvent event = new InteractionEvent(InteractionEvent.Kind.MANIPULATION,
				resourceStructureBridge.getContentType(), fileHandle, "source");
		ContextCorePlugin.getContextManager().processInteractionEvent(event, true);

		node = ContextCore.getContextManager().getElement(fileHandle);
		assertTrue(node.getInterest().isInteresting());

		project.build();
		IProject project = file.getProject();

		String projectHandle = resourceStructureBridge.getHandleIdentifier(project);
		IInteractionElement parentNode = ContextCore.getContextManager().getElement(projectHandle);
		assertFalse(parentNode.getInterest().isInteresting());

		InteractionEvent selectionEvent = new InteractionEvent(InteractionEvent.Kind.SELECTION,
				resourceStructureBridge.getContentType(), fileHandle, "source");
		ContextCorePlugin.getContextManager().processInteractionEvent(selectionEvent, true);

		parentNode = ContextCore.getContextManager().getElement(p1.getHandleIdentifier());
		assertTrue("Package is not in the context", parentNode.getInterest().isInteresting());

		parentNode = ContextCore.getContextManager().getElement(projectHandle);
		assertTrue("Project is not in the context", parentNode.getInterest().isInteresting());
	}

	public void testIncremenOfParentDoi() throws JavaModelException, Exception {
		IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart();
		IMethod m1 = type1.createMethod("void m1() { }", null, true, null);
		IInteractionElement node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertFalse(node.getInterest().isInteresting());

		StructuredSelection sm1 = new StructuredSelection(m1);
		monitor.selectionChanged(part, sm1);
		node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertTrue(node.getInterest().isInteresting());

		project.build();
		IJavaElement parent = m1.getParent();
		int level = 1;
		do {
			level++;
			IInteractionElement parentNode = ContextCore.getContextManager().getElement(parent.getHandleIdentifier());
			if (!(parent instanceof JavaModel)) {
				assertEquals("failed on: " + parent.getClass(), node.getInterest().getValue(), parentNode.getInterest()
						.getValue());
			}
			parent = parent.getParent();
		} while (parent != null);
	}

	public void testIncremenOfParentDoiAfterForcedDecay() throws JavaModelException, Exception {
		IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart();
		IMethod m1 = type1.createMethod("void m1() { }", null, true, null);
		IMethod m2 = type1.createMethod("void m2() { }", null, true, null);
		IInteractionElement node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertFalse(node.getInterest().isInteresting());

		monitor.selectionChanged(part, new StructuredSelection(m1));
		node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertTrue(node.getInterest().isInteresting());

		// make all the parents interest propated to have negative interest
		IJavaElement parent = m1.getParent();
		int level = 1;
		do {
			level++;
			IInteractionElement parentNode = ContextCore.getContextManager().getElement(parent.getHandleIdentifier());
			if (!(parent instanceof JavaModel)) {
				assertTrue(parentNode.getInterest().isInteresting());
				ContextCore.getContextManager().processInteractionEvent(
						mockInterestContribution(parentNode.getHandleIdentifier(), -2
								* parentNode.getInterest().getValue()));
				IInteractionElement updatedParent = ContextCore.getContextManager().getElement(
						parent.getHandleIdentifier());
				assertFalse(updatedParent.getInterest().isInteresting());
			}
			parent = parent.getParent();
		} while (parent != null);

//		assertFalse(node.getInterest().isInteresting());

		// select the element, should propagate up
		monitor.selectionChanged(part, new StructuredSelection(m2));
		monitor.selectionChanged(part, new StructuredSelection(m1));
		node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
		assertTrue(node.getInterest().isInteresting());

		project.build();
		parent = m1.getParent();
		level = 1;
		do {
			level++;
			IInteractionElement parentNode = ContextCore.getContextManager().getElement(parent.getHandleIdentifier());
			if (!(parent instanceof JavaModel)) {
				assertTrue(parentNode.getInterest().isInteresting());
//				assertEquals("failed on: " + parent.getClass(), node.getInterest().getValue(), parentNode.getInterest()
//						.getValue());
			}
			parent = parent.getParent();
		} while (parent != null);
	}

	public void testLandmarks() throws CoreException, IOException {
		LandmarksModelListener listener = new LandmarksModelListener();
		try {
			manager.addListener(listener);

			IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart();
			IMethod m1 = type1.createMethod("void m1() { }", null, true, null);

			StructuredSelection sm1 = new StructuredSelection(m1);
			monitor.selectionChanged(part, sm1);
			manager.processInteractionEvent(mockInterestContribution(m1.getHandleIdentifier(), scaling.getLandmark()));
			// packages can't be landmarks
			manager.processInteractionEvent(mockInterestContribution(m1.getCompilationUnit()
					.getParent()
					.getHandleIdentifier(), scaling.getLandmark()));
			// source folders can't be landmarks
			manager.processInteractionEvent(mockInterestContribution(m1.getCompilationUnit()
					.getParent()
					.getParent()
					.getHandleIdentifier(), scaling.getLandmark()));
			// projects can't be landmarks
			manager.processInteractionEvent(mockInterestContribution(m1.getCompilationUnit()
					.getParent()
					.getParent()
					.getParent()
					.getHandleIdentifier(), scaling.getLandmark()));

			assertEquals(1, ContextCore.getContextManager().getActiveLandmarks().size());
			assertEquals(1, listener.numAdditions);

			manager.processInteractionEvent(mockInterestContribution(m1.getHandleIdentifier(), -scaling.getLandmark()));
			assertEquals(1, listener.numDeletions);
		} finally {
			manager.removeListener(listener);
		}
	}

	public void testEventProcessWithObject() throws JavaModelException {
		InteractionContext context = new InteractionContext("global-id", new InteractionContextScaling());
		context.setContentLimitedTo(JavaStructureBridge.CONTENT_TYPE);
		ContextCorePlugin.getContextManager().addGlobalContext(context);

		assertEquals(0, ContextCore.getContextManager().getActiveContext().getAllElements().size());
		assertEquals(0, context.getAllElements().size());
		ContextCorePlugin.getContextManager().processInteractionEvent(type1, InteractionEvent.Kind.SELECTION,
				MOCK_ORIGIN, context);
		assertEquals(9, context.getAllElements().size());
		assertEquals(0, ContextCore.getContextManager().getActiveContext().getAllElements().size());
		ContextCorePlugin.getContextManager().removeGlobalContext(context);
	}

	public void testEventProcessWithNonExistentObject() throws JavaModelException {
		InteractionContext context = new InteractionContext("global-id", new InteractionContextScaling());
		context.setContentLimitedTo(JavaStructureBridge.CONTENT_TYPE);
		ContextCorePlugin.getContextManager().addGlobalContext(context);

		assertEquals(0, ContextCore.getContextManager().getActiveContext().getAllElements().size());
		assertEquals(0, context.getAllElements().size());
		ContextCorePlugin.getContextManager().processInteractionEvent(new String("non existent"),
				InteractionEvent.Kind.SELECTION, MOCK_ORIGIN, context);
		assertEquals(0, context.getAllElements().size());
		assertEquals(0, ContextCore.getContextManager().getActiveContext().getAllElements().size());
		ContextCorePlugin.getContextManager().removeGlobalContext(context);
	}

	public void testExplicitContextManipulationListener() throws JavaModelException {

		StubContextElementedDeletedListener listener = new StubContextElementedDeletedListener();
		try {
			manager.addListener(listener);

			IMethod m1 = type1.createMethod("void m1() { }", null, true, null);
			IInteractionElement node = ContextCore.getContextManager().getElement(m1.getHandleIdentifier());
			assertFalse(node.getInterest().isInteresting());

			InteractionEvent event = new InteractionEvent(InteractionEvent.Kind.MANIPULATION,
					new JavaStructureBridge().getContentType(), m1.getHandleIdentifier(), "source");
			IInteractionElement element = ContextCorePlugin.getContextManager().processInteractionEvent(event, true);

			// test implicit manipulation
			manager.manipulateInterestForElement(element, true, false, true, "test", false);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);

			manager.manipulateInterestForElement(element, false, false, true, "test", false);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(1, listener.implicitDeletionEventCount);
			listener.reset();

			// test emplicit manipulation
			manager.manipulateInterestForElement(element, true, false, true, "test", false);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);

			manager.manipulateInterestForElement(element, false, false, true, "test", true);
			assertEquals(1, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);
			listener.reset();

			// test implicit deletion
			ContextCorePlugin.getContextManager().processInteractionEvent(event, true);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);

			manager.deleteElements(Collections.singleton(element), false);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(1, listener.implicitDeletionEventCount);
			listener.reset();

			// test explicit deletion
			ContextCorePlugin.getContextManager().processInteractionEvent(event, true);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);

			manager.deleteElements(Collections.singleton(element), true);
			assertEquals(1, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);
			listener.reset();

			// test old deletion
			ContextCorePlugin.getContextManager().processInteractionEvent(event, true);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(0, listener.implicitDeletionEventCount);

			manager.deleteElements(Collections.singleton(element));
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(1, listener.implicitDeletionEventCount);
		} finally {
			// clean up
			manager.removeListener(listener);
		}
	}

	public void testRemoveProjectFromContextRemovesOnlyInteresting() throws JavaModelException {

		StubContextElementedDeletedListener listener = new StubContextElementedDeletedListener();
		try {
			manager.addListener(listener);
			type1.createMethod("void m1() { }", null, true, null);
			type1.createMethod("void m2() { }", null, true, null);
			type1.createMethod("void m4() { }", null, true, null);
			type1.createMethod("void m5() { }", null, true, null);
			IJavaProject project = type1.getJavaProject();
			IInteractionElement node = ContextCore.getContextManager().getElement(project.getHandleIdentifier());
			assertFalse(node.getInterest().isInteresting());

			InteractionEvent event = new InteractionEvent(InteractionEvent.Kind.MANIPULATION,
					new JavaStructureBridge().getContentType(), project.getHandleIdentifier(), "source");
			IInteractionElement element = ContextCorePlugin.getContextManager().processInteractionEvent(event, true);

			// test implicit deletion
			ContextCorePlugin.getContextManager().processInteractionEvent(event, true);
			assertEquals(0, listener.explicitDeletionEventCount);
			assertEquals(0, listener.elementCount);

			// test explicit deletion
			manager.manipulateInterestForElements(Collections.singletonList(element), false, false, false, "test",
					ContextCorePlugin.getContextManager().getActiveContext(), true);
			assertEquals(1, listener.explicitDeletionEventCount);

			// should have 2 element changes.  1 for resources and 1 for java
			assertEquals(2, listener.elementCount);

		} finally {
			// clean up
			manager.removeListener(listener);
		}
	}

	private class StubContextElementedDeletedListener extends AbstractContextListener {

		private int explicitDeletionEventCount;

		private int implicitDeletionEventCount;

		private int elementCount;

		void reset() {
			implicitDeletionEventCount = 0;
			explicitDeletionEventCount = 0;
			elementCount = 0;
		}

		@Override
		public void contextChanged(ContextChangeEvent event) {
			switch (event.getEventKind()) {

			case LANDMARKS_REMOVED:
			case ELEMENTS_DELETED:
				if (event.isExplicitManipulation()) {
					explicitDeletionEventCount++;
				} else {
					implicitDeletionEventCount++;
				}
				elementCount += event.getElements().size();
				break;
			}
		}
	}

}
