blob: d8e205d33ef4fbc7d9b2166dfa47d3a4062096dc [file] [log] [blame]
/*
* Copyright (c) 2008, 2009, 2011, 2012, 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.util.registry;
import org.eclipse.net4j.internal.util.bundle.OM;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.container.Container;
import org.eclipse.net4j.util.container.ContainerEvent;
import org.eclipse.net4j.util.container.IContainerDelta;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
*/
public abstract class Registry<K, V> extends Container<Map.Entry<K, V>>implements IRegistry<K, V>
{
private boolean autoCommit;
private Transaction transaction;
protected Registry(boolean autoCommit)
{
this.autoCommit = autoCommit;
}
protected Registry()
{
this(true);
}
@Override
public boolean isEmpty()
{
return keySet().isEmpty();
}
public int size()
{
return keySet().size();
}
public Set<Entry<K, V>> entrySet()
{
return getMap().entrySet();
}
public Set<K> keySet()
{
return getMap().keySet();
}
public Collection<V> values()
{
return getMap().values();
}
public boolean containsKey(Object key)
{
return keySet().contains(key);
}
public boolean containsValue(Object value)
{
return values().contains(value);
}
public V get(Object key)
{
return getMap().get(key);
}
/**
* Requires {@link #commit()} to be called later if not {@link #isAutoCommit()}.
*/
public synchronized V put(K key, V value)
{
V result = register(key, value);
autoCommit();
return result;
}
/**
* Requires {@link #commit()} to be called later if not {@link #isAutoCommit()}.
*/
public synchronized void putAll(Map<? extends K, ? extends V> t)
{
if (!t.isEmpty())
{
Iterator<? extends Entry<? extends K, ? extends V>> i = t.entrySet().iterator();
while (i.hasNext())
{
Entry<? extends K, ? extends V> e = i.next();
register(e.getKey(), e.getValue());
}
autoCommit();
}
}
/**
* Requires {@link #commit()} to be called later if not {@link #isAutoCommit()}.
*/
public synchronized V remove(Object key)
{
V result = deregister(key);
autoCommit();
return result;
}
/**
* Requires {@link #commit()} to be called later if not {@link #isAutoCommit()}.
*/
public synchronized void clear()
{
if (!isEmpty())
{
for (Object key : keySet().toArray())
{
deregister(key);
}
autoCommit();
}
}
@SuppressWarnings("unchecked")
public Entry<K, V>[] getElements()
{
return entrySet().toArray(new Entry[size()]);
}
public boolean isAutoCommit()
{
return autoCommit;
}
public void setAutoCommit(boolean autoCommit)
{
this.autoCommit = autoCommit;
}
public synchronized void commit(boolean notifications)
{
if (transaction != null)
{
if (!transaction.isOwned())
{
OM.LOG.warn("Committing thread is not owner of transaction: " + Thread.currentThread()); //$NON-NLS-1$
}
transaction.commit(notifications);
transaction = null;
notifyAll();
}
}
public void commit()
{
commit(true);
}
@Override
public String toString()
{
return getMap().toString();
}
protected V register(K key, V value)
{
Transaction transaction = getTransaction();
V oldValue = getMap().put(key, value);
if (oldValue != null)
{
transaction.rememberDeregistered(key, oldValue);
}
transaction.rememberRegistered(key, value);
return oldValue;
}
@SuppressWarnings("unchecked")
protected V deregister(Object key)
{
V value = getMap().remove(key);
if (value != null)
{
getTransaction().rememberDeregistered((K)key, value);
}
return value;
}
protected Transaction getTransaction()
{
for (;;)
{
if (transaction == null)
{
transaction = new Transaction();
return transaction;
}
if (transaction.isOwned())
{
transaction.increaseNesting();
return transaction;
}
try
{
wait();
}
catch (InterruptedException ex)
{
throw WrappedException.wrap(ex);
}
}
}
protected void autoCommit()
{
if (autoCommit)
{
commit();
}
}
protected abstract Map<K, V> getMap();
/**
* @author Eike Stepper
*/
protected class Transaction
{
private int nesting = 1;
private ContainerEvent<Map.Entry<K, V>> event;
private Thread owner;
public Transaction()
{
owner = Thread.currentThread();
initEvent();
}
private void initEvent()
{
event = newContainerEvent();
}
public boolean isOwned()
{
return owner == Thread.currentThread();
}
public void increaseNesting()
{
++nesting;
}
public void commit(boolean notifications)
{
if (--nesting == 0)
{
if (notifications && !event.isEmpty())
{
fireEvent(event);
}
initEvent();
}
}
public void rememberRegistered(K key, V value)
{
event.addDelta(new Element<K, V>(key, value), IContainerDelta.Kind.ADDED);
}
public void rememberDeregistered(K key, V value)
{
event.addDelta(new Element<K, V>(key, value), IContainerDelta.Kind.REMOVED);
}
}
/**
* @author Eike Stepper
*/
private static final class Element<K, V> implements Map.Entry<K, V>
{
private final K key;
private final V value;
private Element(K key, V value)
{
this.key = key;
this.value = value;
}
public K getKey()
{
return key;
}
public V getValue()
{
return value;
}
public V setValue(V value)
{
throw new UnsupportedOperationException();
}
}
}