/*-
 * Copyright 2015, 2016 Diamond Light Source Ltd.
 *
 * 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
 */

package org.eclipse.january.dataset;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Class used by DynamicDataset to delegate
 */
public class DataListenerDelegate {

	private List<IDataListener> listeners;

	public DataListenerDelegate() {
		listeners = Collections.synchronizedList(new ArrayList<IDataListener>());
	}
	
	public void addDataListener(IDataListener l) {
		synchronized (listeners) {
			if (!listeners.contains(l)) {
				listeners.add(l);
			}
		}
	}

	public void removeDataListener(IDataListener l) {
		listeners.remove(l);
	}

	public void fire(DataEvent evt) {
		synchronized (listeners) {
			for (Iterator<IDataListener> iterator = listeners.iterator(); iterator.hasNext();) {
				iterator.next().dataChangePerformed(evt);
			}
		}
	}

	public boolean hasDataListeners() {
		return listeners.size() > 0;
	}

	public void clear() {
		listeners.clear();
 	}

}

/*-
 * Copyright 2015, 2016 Diamond Light Source Ltd.
 *
 * 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
 */

package org.eclipse.january.dataset;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Class used by DynamicDataset to delegate
 */
public class DataListenerDelegate {

	private Set<IDataListener> listeners;

	public DataListenerDelegate() {
		listeners = Collections.newSetFromMap(new ConcurrentHashMap<IDataListener, Boolean>());
	}

	public void addDataListener(IDataListener l) {
		listeners.add(l);
	}

	public void removeDataListener(IDataListener l) {
		listeners.remove(l);
	}

	public void fire(DataEvent evt) {
		for (IDataListener listener : listeners) {
			listener.dataChangePerformed(evt);
		}
	}

	public boolean hasDataListeners() {
		return listeners.size() > 0;
	}

	public void clear() {
		listeners.clear();
	}

}

Improve thread safety around adding and removing IDataListener's
Improve thread safety around adding and removing IDataListener's

Previously deadlock was possible if a listener was running in the same
thread as a call attempting to add or remove listeners. As might be
commonly the case for the UI thread.

Also replaced the listeners List with a Set to prevent duplicate
listeners being added. Using a set backed by a ConcurrentHashMap should
allow all operation to complete safely without requiring external
synchronized, this allows the methods in the class to be simplified.

Signed-off-by: James Mudd <50b9fff9a292b5df2aef2220dca201538f796e13@diamond.ac.uk>