package utils;

import org.springframework.util.MultiValueMap;

import java.util.List;
import java.util.Map;

/**
 * A Fluent API for manipulating arbitrary maps<br/>
 *<br/>
 * Example usage:<br/>
 * <code>
 * headers = fromMap( new LinkedMultiValueMap<String, String>() )<br/>
 *              .add( "Location", "http://server/path" )<br/>
 *              .add( "Content-Type", "application/json" )<br/>
 *              .add( "ETag", "1234567890" )<br/>
 *              .toMap()<br/>
 * </code>
 * @author Erich Eichinger
 * @since 03/01/13
 */
public final class MapBuilderSupport
{
    public interface MapBuilder<K,V, M extends Map<K,V>> {
        MapBuilder<K,V,M> put(K k, V v);
        M toMap();
    }

    public interface MultiValueMapBuilder<K,V,M extends Map<K,List<V>>> extends MapBuilder<K,List<V>,M> {
        MultiValueMapBuilder<K,V,M> put(K k, List<V> v);
        MultiValueMapBuilder<K,V,M> add(K k, V v);
    }

    public static <K,V,M extends Map<K,V>> MapBuilder<K,V,M> fromMap(M map) {
        return new SingleValueMapBuilder<K,V,M>(map);
    }

    public static <K,V,M extends org.springframework.util.MultiValueMap<K,V>> MultiValueMapBuilder<K,V,M> fromMap(M map) {
        return new MultiValueMapBuilderImpl<K,V,M>(map);
    }

    private static class SingleValueMapBuilder<K,V,M extends Map<K,V>> implements MapBuilder<K,V,M> {
        final protected M map;

        private SingleValueMapBuilder( M map ) {
            this.map = map;
        }

        public MapBuilder<K,V,M> put(K key, V val) {
            map.put( key, val );
            return this;
        }

        public M toMap() {
            return map;
        }
    }

    private static class MultiValueMapBuilderImpl<K,V,M extends MultiValueMap<K,V>> extends SingleValueMapBuilder<K,List<V>, M> implements MultiValueMapBuilder<K,V,M> {

        private MultiValueMapBuilderImpl( M map ) {
            super(map);
        }

        @Override
        public MultiValueMapBuilder<K,V,M> add(K key, V val) {
            map.add( key, val );
            return this;
        }

        @Override
        public MultiValueMapBuilder<K, V, M> put( K k, List<V> vs ) {
            map.put( k, vs );
            return this;
        }

        public M toMap() {
            return map;
        }
    }
}