package org.ovirt.engine.core.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A utility class for manipulating maps that have a value of a {@link List} type.
 */
public class MultiValueMapUtils {

    /** A private constructor to prohibit instantiation */
    private MultiValueMapUtils() {
    }

    /**
     * Adds an additional {@link #value} to the given {@link #key} in the {@link #map}.
     *
     * If a collection of values was already associated with this key, the new value is added to its end.
     * It's assumed that the existing list implementation is mutable.
     *
     * If there is no collection associated with this value, a new collection containing the value will be created.
     *
     * @param key The key of the map to which the value should be added.
     * @param value The value to add to the map
     * @param map The map to have the value added
     * @param creator A factory to create the new (empty) collection for the values to be held
     */
    public static <K, V, C extends Collection<V>> void addToMap(K key,
            V value,
            Map<K, C> map,
            CollectionCreator<V, C> creator) {
        C existingValues = map.get(key);
        if (existingValues == null) {
            existingValues = creator.create();
            map.put(key, existingValues);
        }
        existingValues.add(value);
    }

    /**
     * Same as {@link #addToMap(Object, Object, Map, CollectionCreator)}, with the default {@link ArrayList} to hold the values.
     */
    public static <K, V> void addToMap(K key, V value, Map<K, List<V>> map) {
        addToMap(key, value, map, new ListCreator<V>());
    }

    public static interface CollectionCreator<V, C extends Collection<V>> {
        C create();
    }

    public static class ListCreator<V> implements CollectionCreator<V, List<V>> {
        @Override
        public List<V> create() {
            return new ArrayList<V>();
        }
    }

    public static class ArrayListCreator<V> implements CollectionCreator<V, ArrayList<V>> {
        @Override
        public ArrayList<V> create() {
            return new ArrayList<V>();
        }
    }

    public static class LinkedListCreator<V> implements CollectionCreator<V, LinkedList<V>> {
        @Override
        public LinkedList<V> create() {
            return new LinkedList<V>();
        }
    }

    public static class SetCreator<V> implements CollectionCreator<V, Set<V>> {
        @Override
        public Set<V> create() {
            return new HashSet<V>();
        }
    }

    public static class HashSetCreator<V> implements CollectionCreator<V, HashSet<V>> {
        @Override
        public HashSet<V> create() {
            return new HashSet<V>();
        }
    }
}
