cern.entwined.TransactionalMultimap.java Source code

Java tutorial

Introduction

Here is the source code for cern.entwined.TransactionalMultimap.java

Source

/*
 * Entwined STM
 * 
 * (c) Copyright 2013 CERN. This software is distributed under the terms of the Apache License Version 2.0, copied
 * verbatim in the file "COPYING". In applying this licence, CERN does not waive the privileges and immunities granted
 * to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction.
 */
package cern.entwined;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

/**
 * The opaque (impossible to query for all the items or get the size of the collection) transactional multimap
 * implementation.
 * 
 * @author Ivan Koblik
 */
public class TransactionalMultimap<K, V> extends SemiPersistent<TransactionalMultimap<K, V>>
        implements OpaqueMultimap<K, V> {

    /**
     * The {@link TransactionalMap} instance actually storing the data.
     */
    private final TransactionalMap<K, Set<V>> delegate;

    /**
     * Constructs a new empty {@link TransactionalMultimap}.
     */
    public TransactionalMultimap() {
        this(new TransactionalMap<K, Set<V>>());
    }

    private TransactionalMultimap(TransactionalMap<K, Set<V>> source) {
        this.delegate = source;
    }

    //
    // Overridden methods
    //

    /**
     * Delegates to {@link TransactionalMap#size()}.
     */
    @Override
    public int size() {
        return this.delegate.size();
    }

    /**
     * Delegates to {@link TransactionalMap#isEmpty()}.
     */
    @Override
    public boolean isEmpty() {
        return this.delegate.isEmpty();
    }

    /**
     * Delegates to {@link TransactionalMap#get(Object)} replacing the returned <code>null</code> value with an empty
     * set.
     */
    @Override
    public Set<V> get(K key) {
        Set<V> result = this.delegate.get(key);
        if (null != result) {
            return result;
        } else {
            return ImmutableSet.<V>of();
        }
    };

    /**
     * Stores given pair in the map, extending set associated with the key, if it already existed. This method doesn't
     * perform null checks and doesn't copy the given collection.
     * 
     * @param key The key under which to store the values.
     * @param value The values for the key.
     * @return The old set of values replaced by the method.
     */
    private Set<V> unsafePut(K key, Set<V> value) {
        if (value.isEmpty()) {
            return this.remove(key);
        } else {
            Set<V> replaced = this.delegate.put(key, Collections.unmodifiableSet(value));
            return null != replaced ? replaced : ImmutableSet.<V>of();
        }
    };

    /**
     * Delegates to {@link TransactionalMap#remove(Object)} replacing the returned <code>null</code> value with an empty
     * set.
     */
    @Override
    public Set<V> remove(K key) {
        Set<V> result = this.delegate.remove(key);
        return null != result ? result : ImmutableSet.<V>of();
    };

    @Override
    public void clear() {
        this.delegate.clear();
    }

    @Override
    public boolean containsKey(K key) {
        return this.delegate.containsKey(key);
    };

    /**
     * Overrides the default behavior of the method by merging the immutable sets for the same key.
     */
    @Override
    public void putAll(Map<? extends K, ? extends Set<V>> map) {
        Utils.checkNull("Inserted map", map);
        for (Entry<? extends K, ? extends Set<V>> pair : map.entrySet()) {
            this.putAll(pair.getKey(), pair.getValue());
        }
    }

    //
    // Views
    //

    /**
     * Delegates to {@link TransactionalMap#keySet()}.
     */
    @Override
    public Set<K> keySet() {
        return this.delegate.keySet();
    }

    //
    // Additional methods
    //

    /**
     * Adds the value to the entry with the given key.
     * 
     * @param key The key that the set is stored under.
     * @param value The value that is added to the entry with the given key.
     * @return The replaced set or an empty set if there was no entry with the given key.
     */
    @Override
    public Set<V> put(K key, V value) {
        Set<V> oldSet = this.delegate.get(key);
        if (null != oldSet) {
            HashSet<V> newSet = new HashSet<V>(oldSet);
            newSet.add(value);
            return this.unsafePut(key, newSet);
        } else {
            return this.unsafePut(key, Collections.singleton(value));
        }
    };

    /**
     * Puts all the values in the collection with the given key.
     * 
     * @param key The key that the set is stored under.
     * @param values The values that are added to the entry with the given key.
     * @return The replaced set or an empty set if there was no entry with the given key.
     */
    public Set<V> putAll(K key, Collection<V> values) {
        Utils.checkNull("Values", values);
        Set<V> oldSet = this.delegate.get(key);
        HashSet<V> newSet;
        if (null != oldSet) {
            newSet = new HashSet<V>(oldSet);
            newSet.addAll(values);
        } else {
            newSet = new HashSet<V>(values);
        }
        return this.unsafePut(key, newSet);
    }

    /**
     * Removes the given value from the entry with the given key. If there are no more values for the key, removes the
     * whole entry from the map.
     * 
     * @param key The key to retrieve the set.
     * @param value Value to be removed from the set.
     * @return The replaced set or an empty set if there was no entry with the given key.
     */
    public Set<V> remove(K key, V value) {
        Set<V> oldSet = this.delegate.get(key);
        if (null != oldSet) {
            Set<V> newSet = Sets.difference(oldSet, Collections.singleton(value)).immutableCopy();
            return this.unsafePut(key, newSet);
        }
        return this.remove(key);
    };

    //
    // Transactional methods
    //

    @Override
    public TransactionalMultimap<K, V> commit(TransactionalMultimap<K, V> globalState) {
        return new TransactionalMultimap<K, V>(this.delegate.commit(globalState.delegate));
    }

    @Override
    protected TransactionalMultimap<K, V> cleanCopy() {
        return new TransactionalMultimap<K, V>(this.delegate.cleanCopy());
    }

    @Override
    protected TransactionalMultimap<K, V> dirtyCopy() {
        return new TransactionalMultimap<K, V>(this.delegate.dirtyCopy());
    }

    @Override
    protected void update(TransactionalMultimap<K, V> changes, boolean onlyReadLogs) {
        this.delegate.update(changes.delegate, onlyReadLogs);
    }
}