Skip to main content
aboutsummaryrefslogblamecommitdiffstats
blob: f8fa0b76b3a0fa4fc78133325b011170ec395f48 (plain) (tree)
1
2
  
                                                                                        























                                                                        
                                                                                                  












































































































































































































































































































                                                                                                              
/*
 * 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();
    }
  }
}

Back to the top