All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atlassian.util.concurrent.AbstractCopyOnWriteMap Maven / Gradle / Ivy

Go to download

This project contains utility classes that are used by various products and projects inside Atlassian and may have some utility to the world at large.

There is a newer version: 4.0.1
Show newest version
/**
 * Copyright 2008 Atlassian Pty Ltd 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package io.atlassian.util.concurrent;

import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;

/**
 * Abstract base class for COW {@link Map} implementations that delegate to an
 * internal map.
 * 
 * @param  The key type
 * @param  The value type
 * @param  the internal {@link Map} or extension for things like sorted and
 * navigable maps.
 */
@ThreadSafe abstract class AbstractCopyOnWriteMap> implements ConcurrentMap, Serializable {
  private static final long serialVersionUID = 4508989182041753878L;

  @GuardedBy("lock") private volatile M delegate;

  private final transient Lock lock = new ReentrantLock();

  private final View view;

  /**
   * Create a new {@link io.atlassian.util.concurrent.CopyOnWriteMap} with the
   * supplied {@link java.util.Map} to initialize the values.
   *
   * @param map the initial map to initialize with
   * @param viewType for writable or read-only key, value and entrySet views
   * @param  original map type.
   */
  protected > AbstractCopyOnWriteMap(final N map, final View.Type viewType) {
    this.delegate = requireNonNull(copy(requireNonNull(map, "map")), "delegate");
    this.view = requireNonNull(viewType, "viewType").get(this);
  }

  /**
   * Copy function, implemented by sub-classes.
   * 
   * @param  the map to copy and return.
   * @param map the initial values of the newly created map.
   * @return a new map. Will never be modified after construction.
   */
  @GuardedBy("lock") abstract > M copy(N map);

  //
  // mutable operations
  //

  /**
   * Return a new copy containing no elements
   */
  public final void clear() {
    lock.lock();
    try {
      set(copy(Collections. emptyMap()));
    } finally {
      lock.unlock();
    }
  }

  /** {@inheritDoc} */
  public final V remove(final Object key) {
    lock.lock();
    try {
      // short circuit if key doesn't exist
      if (!delegate.containsKey(key)) {
        return null;
      }
      final M map = copy();
      try {
        return map.remove(key);
      } finally {
        set(map);
      }
    } finally {
      lock.unlock();
    }
  }

  /** {@inheritDoc} */
  public final boolean remove(final Object key, final Object value) {
    lock.lock();
    try {
      if (delegate.containsKey(key) && equals(value, delegate.get(key))) {
        final M map = copy();
        map.remove(key);
        set(map);
        return true;
      } else {
        return false;
      }
    } finally {
      lock.unlock();
    }
  }

  /**
   * Attempt to replace the value of the key with newValues so long as the key
   * is still associated with old value
   *
   * @param key a K key to to define a new association for.
   * @param oldValue if this value is still present replace it with newValue.
   * @param newValue a V to add to the map.
   * @return a boolean if the replacement is successful
   */
  public final boolean replace(final K key, final V oldValue, final V newValue) {
    lock.lock();
    try {
      if (!delegate.containsKey(key) || !equals(oldValue, delegate.get(key))) {
        return false;
      }
      final M map = copy();
      map.put(key, newValue);
      set(map);
      return true;
    } finally {
      lock.unlock();
    }
  }

  /**
   * Replace the value of the key if the key is present returning the value.
   *
   * @param key a K key.
   * @param value a V value.
   * @return a V if the key is present or null if it is not.
   */
  public final V replace(final K key, final V value) {
    lock.lock();
    try {
      if (!delegate.containsKey(key)) {
        return null;
      }
      final M map = copy();
      try {
        return map.put(key, value);
      } finally {
        set(map);
      }
    } finally {
      lock.unlock();
    }
  }

  /**
   * Add this key and its value to the map
   *
   * @param key a K key.
   * @param value a V value.
   * @return a V value added.
   */
  public final V put(final K key, final V value) {
    lock.lock();
    try {
      final M map = copy();
      try {
        return map.put(key, value);
      } finally {
        set(map);
      }
    } finally {
      lock.unlock();
    }
  }

  /**
   * If the key is not currently contained in the map add it. Returns the
   * current value associated with the key whether or not the put succeeds.
   *
   * @param key a K key.
   * @param value a V value.
   * @return a V associated with the key, may or may not be the input value to
   * this method.
   */
  public final V putIfAbsent(final K key, final V value) {
    lock.lock();
    try {
      if (!delegate.containsKey(key)) {
        final M map = copy();
        try {
          return map.put(key, value);
        } finally {
          set(map);
        }
      }
      return delegate.get(key);
    } finally {
      lock.unlock();
    }
  }

  /** {@inheritDoc} */
  public final void putAll(final Map t) {
    lock.lock();
    try {
      final M map = copy();
      map.putAll(t);
      set(map);
    } finally {
      lock.unlock();
    }
  }

  /**
   * Create a copy of the underlying map.
   *
   * @return a M map.
   */
  protected M copy() {
    lock.lock();
    try {
      return copy(delegate);
    } finally {
      lock.unlock();
    }
  }

  /**
   * Set the contained map
   *
   * @param map a M contained by this class.
   */
  @GuardedBy("lock") protected final void set(final M map) {
    delegate = map;
  }

  //
  // Collection views
  //

  /** {@inheritDoc} */
  public final Set> entrySet() {
    return view.entrySet();
  }

  /** {@inheritDoc} */
  public final Set keySet() {
    return view.keySet();
  }

  /** {@inheritDoc} */
  public final Collection values() {
    return view.values();
  }

  //
  // delegate operations
  //

  /** {@inheritDoc} */
  public final boolean containsKey(final Object key) {
    return delegate.containsKey(key);
  }

  /** {@inheritDoc} */
  public final boolean containsValue(final Object value) {
    return delegate.containsValue(value);
  }

  /** {@inheritDoc} */
  public final V get(final Object key) {
    return delegate.get(key);
  }

  /** {@inheritDoc} */
  public final boolean isEmpty() {
    return delegate.isEmpty();
  }

  /** {@inheritDoc} */
  public final int size() {
    return delegate.size();
  }

  /** {@inheritDoc} */
  @Override public final boolean equals(final Object o) {
    return delegate.equals(o);
  }

  /** {@inheritDoc} */
  @Override public final int hashCode() {
    return delegate.hashCode();
  }

  /**
   * Return the internal delegate map.
   *
   * @return a M map.
   */
  protected final M getDelegate() {
    return delegate;
  }

  /** {@inheritDoc} */
  @Override public String toString() {
    return delegate.toString();
  }

  //
  // inner classes
  //

  private class KeySet extends CollectionView implements Set {

    @Override Collection getDelegate() {
      return delegate.keySet();
    }

    //
    // mutable operations
    //

    public void clear() {
      lock.lock();
      try {
        final M map = copy();
        map.keySet().clear();
        set(map);
      } finally {
        lock.unlock();
      }
    }

    public boolean remove(final Object o) {
      return AbstractCopyOnWriteMap.this.remove(o) != null;
    }

    public boolean removeAll(final Collection c) {
      lock.lock();
      try {
        final M map = copy();
        try {
          return map.keySet().removeAll(c);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }

    public boolean retainAll(final Collection c) {
      lock.lock();
      try {
        final M map = copy();
        try {
          return map.keySet().retainAll(c);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }
  }

  private final class Values extends CollectionView {

    @Override Collection getDelegate() {
      return delegate.values();
    }

    public void clear() {
      lock.lock();
      try {
        final M map = copy();
        map.values().clear();
        set(map);
      } finally {
        lock.unlock();
      }
    }

    public boolean remove(final Object o) {
      lock.lock();
      try {
        if (!contains(o)) {
          return false;
        }
        final M map = copy();
        try {
          return map.values().remove(o);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }

    public boolean removeAll(final Collection c) {
      lock.lock();
      try {
        final M map = copy();
        try {
          return map.values().removeAll(c);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }

    public boolean retainAll(final Collection c) {
      lock.lock();
      try {
        final M map = copy();
        try {
          return map.values().retainAll(c);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }
  }

  private class EntrySet extends CollectionView> implements Set> {

    @Override Collection> getDelegate() {
      return delegate.entrySet();
    }

    public void clear() {
      lock.lock();
      try {
        final M map = copy();
        map.entrySet().clear();
        set(map);
      } finally {
        lock.unlock();
      }
    }

    public boolean remove(final Object o) {
      lock.lock();
      try {
        if (!contains(o)) {
          return false;
        }
        final M map = copy();
        try {
          return map.entrySet().remove(o);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }

    public boolean removeAll(final Collection c) {
      lock.lock();
      try {
        final M map = copy();
        try {
          return map.entrySet().removeAll(c);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }

    public boolean retainAll(final Collection c) {
      lock.lock();
      try {
        final M map = copy();
        try {
          return map.entrySet().retainAll(c);
        } finally {
          set(map);
        }
      } finally {
        lock.unlock();
      }
    }
  }

  private static class UnmodifiableIterator implements Iterator {
    private final Iterator delegate;

    public UnmodifiableIterator(final Iterator delegate) {
      this.delegate = delegate;
    }

    public boolean hasNext() {
      return delegate.hasNext();
    }

    public T next() {
      return delegate.next();
    }

    public void remove() {
      throw new UnsupportedOperationException();
    }
  }

  protected static abstract class CollectionView implements Collection {

    abstract Collection getDelegate();

    //
    // delegate operations
    //

    public final boolean contains(final Object o) {
      return getDelegate().contains(o);
    }

    public final boolean containsAll(final Collection c) {
      return getDelegate().containsAll(c);
    }

    public final Iterator iterator() {
      return new UnmodifiableIterator(getDelegate().iterator());
    }

    public final boolean isEmpty() {
      return getDelegate().isEmpty();
    }

    public final int size() {
      return getDelegate().size();
    }

    public final Object[] toArray() {
      return getDelegate().toArray();
    }

    public final  T[] toArray(final T[] a) {
      return getDelegate().toArray(a);
    }

    @Override public int hashCode() {
      return getDelegate().hashCode();
    }

    @Override public boolean equals(final Object obj) {
      return getDelegate().equals(obj);
    }

    @Override public String toString() {
      return getDelegate().toString();
    }

    //
    // unsupported operations
    //

    public final boolean add(final E o) {
      throw new UnsupportedOperationException();
    }

    public final boolean addAll(final Collection c) {
      throw new UnsupportedOperationException();
    }
  }

  private boolean equals(final Object o1, final Object o2) {
    if (o1 == null) {
      return o2 == null;
    }
    return o1.equals(o2);
  }

  /**
   * Provides access to the views of the underlying key, value and entry
   * collections.
   */
  public static abstract class View {
    View() {}

    abstract Set keySet();

    abstract Set> entrySet();

    abstract Collection values();

    /**
     * The different types of {@link View} available
     */
    public enum Type {
      STABLE {
        @Override > View get(final AbstractCopyOnWriteMap host) {
          return host.new Immutable();
        }
      },
      LIVE {
        @Override > View get(final AbstractCopyOnWriteMap host) {
          return host.new Mutable();
        }
      };
      abstract > View get(AbstractCopyOnWriteMap host);
    }
  }

  final class Immutable extends View implements Serializable {

    private static final long serialVersionUID = -4158727180429303818L;

    @Override public Set keySet() {
      return unmodifiableSet(delegate.keySet());
    }

    @Override public Set> entrySet() {
      return unmodifiableSet(delegate.entrySet());
    }

    @Override public Collection values() {
      return unmodifiableCollection(delegate.values());
    }
  }

  final class Mutable extends View implements Serializable {

    private static final long serialVersionUID = 1624520291194797634L;

    private final transient KeySet keySet = new KeySet();
    private final transient EntrySet entrySet = new EntrySet();
    private final transient Values values = new Values();

    @Override public Set keySet() {
      return keySet;
    }

    @Override public Set> entrySet() {
      return entrySet;
    }

    @Override public Collection values() {
      return values;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy