Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/registry/Registry.java')
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/registry/Registry.java328
1 files changed, 328 insertions, 0 deletions
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/registry/Registry.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/registry/Registry.java
new file mode 100644
index 0000000000..3342476a22
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/registry/Registry.java
@@ -0,0 +1,328 @@
+/***************************************************************************
+ * Copyright (c) 2004 - 2007 Eike Stepper, Germany.
+ * 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.internal.util.registry;
+
+import org.eclipse.net4j.internal.util.bundle.OM;
+import org.eclipse.net4j.internal.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.container.IContainerDelta;
+import org.eclipse.net4j.util.registry.IRegistry;
+
+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 Lifecycle implements IRegistry<K, V>
+{
+ private boolean autoCommit;
+
+ private Transaction transaction;
+
+ protected Registry(boolean autoCommit)
+ {
+ this.autoCommit = autoCommit;
+ }
+
+ protected Registry()
+ {
+ this(true);
+ }
+
+ 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();
+ }
+ }
+
+ 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());
+ }
+
+ 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;
+ }
+
+ 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)
+ {
+ return null;
+ }
+ }
+ }
+
+ protected void autoCommit()
+ {
+ if (autoCommit)
+ {
+ commit();
+ }
+ }
+
+ protected abstract Map<K, V> getMap();
+
+ /**
+ * @author Eike Stepper
+ */
+ protected class Transaction
+ {
+ private int nesting = 1;
+
+ private RegistryEvent<K, V> event;
+
+ private Thread owner;
+
+ public Transaction()
+ {
+ owner = Thread.currentThread();
+ initEvent();
+ }
+
+ private void initEvent()
+ {
+ event = new RegistryEvent(Registry.this);
+ }
+
+ 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();
+ }
+ }
+}

Back to the top