blob: 73570e9352acb414630afb202efd654ed6557ccd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2005 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.preferences;
import org.eclipse.core.runtime.ListenerList;
/**
* A class which holds onto a listener list object for a given path.
* Typically the path is the absolute path of a preference node.
*
* @since 3.1
*/
public class ListenerRegistry {
/**
* Specialized map-like data structure for storing change listeners.
*/
private static class ListenerMap {
private static final int GROW_SIZE = 10;
String[] keys;
ListenerList[] values;
/**
* Create a map of exactly the specified size.
*/
ListenerMap(int size) {
super();
this.keys = new String[size];
this.values = new ListenerList[size];
}
/**
* Return the listener list associated with the given key,
* or <code>null</code> if it doesn't exist.
*/
ListenerList get(String key) {
if (key == null)
throw new NullPointerException();
for (int i = 0; i < keys.length; i++)
if (key.equals(keys[i]))
return values[i];
return null;
}
/**
* Associate the given listener list with the specified key. Overwrite
* an existing association, if applicable.
*/
void put(String key, ListenerList value) {
if (key == null)
throw new NullPointerException();
if (value == null) {
remove(key);
return;
}
// replace if exists, keeping track of an empty position
int emptyIndex = -1;
for (int i = 0; i < keys.length; i++) {
String existing = keys[i];
if (existing == null) {
emptyIndex = i;
continue;
}
if (existing.equals(key)) {
values[i] = value;
return;
}
}
if (emptyIndex == -1)
emptyIndex = grow();
keys[emptyIndex] = key;
values[emptyIndex] = value;
}
/*
* Make the backing arrays larger
*/
private int grow() {
int size = keys.length;
String[] tempKeys = new String[size + GROW_SIZE];
System.arraycopy(keys, 0, tempKeys, 0, size);
keys = tempKeys;
ListenerList[] tempValues = new ListenerList[size + GROW_SIZE];
System.arraycopy(values, 0, tempValues, 0, size);
values = tempValues;
return size;
}
/**
* Remove the association specified by the given key.
* Do nothing if none exists.
*
* Note: Should consider shrinking the array. Hold off for now
* as we don't expect #remove to be a common code path.
*/
void remove(String key) {
if (key == null)
throw new NullPointerException();
for (int i = 0; i < keys.length; i++)
if (key.equals(keys[i])) {
keys[i] = null;
values[i] = null;
return;
}
}
}
static final Object[] EMPTY_LIST = new Object[0];
ListenerMap registry = new ListenerMap(25);
/**
* Return the listeners for this path or an empty list if none.
*/
public synchronized Object[] getListeners(String path) {
ListenerList list = registry.get(path);
return list == null ? EMPTY_LIST : list.getListeners();
}
/**
* Add the given listener to the listeners registered for this path.
* If the listener already exists, then do nothing.
*/
public synchronized void add(String path, Object listener) {
ListenerList list = registry.get(path);
if (list == null)
list = new ListenerList(ListenerList.IDENTITY);
list.add(listener);
registry.put(path, list);
}
/**
* Remove the given listener from this path's collection of
* listeners. If it is not associated with this path, then do nothing.
*/
public synchronized void remove(String path, Object listener) {
ListenerList list = registry.get(path);
if (list == null)
return;
list.remove(listener);
if (list.isEmpty())
registry.remove(path);
}
/**
* Remove all of the listeners registered under the given path.
*/
public synchronized void clear(String path) {
registry.remove(path);
}
}