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

aQute.lib.collections.MultiMap Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package aQute.lib.collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class MultiMap extends LinkedHashMap> implements Map> {
	private static final long	serialVersionUID	= 1L;
	private final boolean		noduplicates;
	private final Class		keyClass;
	private final Class		valueClass;

	public MultiMap() {
		this(false);
	}

	public MultiMap(boolean noduplicates) {
		this.noduplicates = noduplicates;
		keyClass = Object.class;
		valueClass = Object.class;
	}

	public MultiMap(Class keyClass, Class valueClass, boolean noduplicates) {
		this.noduplicates = noduplicates;
		this.keyClass = keyClass;
		this.valueClass = valueClass;
	}

	public  MultiMap(Map> other) {
		this();
		for (java.util.Map.Entry> e : other.entrySet()) {
			addAll(e.getKey(), e.getValue());
		}
	}

	public  MultiMap(MultiMap other) {
		keyClass = other.keyClass;
		valueClass = other.valueClass;
		noduplicates = other.noduplicates;
		for (java.util.Map.Entry> e : other.entrySet()) {
			addAll(e.getKey(), e.getValue());
		}
	}

	@SuppressWarnings("unchecked")
	public boolean add(K key, V value) {
		assert keyClass.isInstance(key);
		assert valueClass.isInstance(value);

		List set = get(key);
		if (set == null) {
			set = new ArrayList<>();
			if (valueClass != Object.class) {
				set = Collections.checkedList(set, (Class) valueClass);
			}
			put(key, set);
		} else {
			if (noduplicates) {
				if (set.contains(value))
					return false;
			}
		}
		return set.add(value);
	}

	@SuppressWarnings("unchecked")
	public boolean addAll(K key, Collection value) {

		if (value == null)
			return false;

		assert keyClass.isInstance(key);
		List set = get(key);
		if (set == null) {
			set = new ArrayList<>();
			if (valueClass != Object.class) {
				set = Collections.checkedList(set, (Class) valueClass);
			}
			put(key, set);
		} else if (noduplicates) {
			boolean r = false;
			for (V v : value) {
				assert valueClass.isInstance(v);
				if (!set.contains(v))
					r |= set.add(v);
			}
			return r;
		}
		return set.addAll(value);
	}

	public boolean addAll(Map> map) {
		boolean added = false;
		for (java.util.Map.Entry> e : map.entrySet()) {
			added |= addAll(e.getKey(), e.getValue());
		}
		return added;
	}

	public boolean removeValue(K key, V value) {
		assert keyClass.isInstance(key);
		assert valueClass.isInstance(value);

		List set = get(key);
		if (set == null) {
			return false;
		}
		boolean result = set.remove(value);
		if (set.isEmpty())
			remove(key);
		return result;
	}

	public boolean removeAll(K key, Collection value) {
		assert keyClass.isInstance(key);
		List set = get(key);
		if (set == null) {
			return false;
		}
		boolean result = set.removeAll(value);
		if (set.isEmpty())
			remove(key);
		return result;
	}

	public Iterator iterate(K key) {
		assert keyClass.isInstance(key);
		List set = get(key);
		if (set == null)
			return Collections. emptyList()
				.iterator();
		return set.iterator();
	}

	public Iterator all() {
		return new Iterator() {
			Iterator>	master	= values().iterator();
			Iterator			current	= null;

			@Override
			public boolean hasNext() {
				if (current == null || !current.hasNext()) {
					if (master.hasNext()) {
						current = master.next()
							.iterator();
						return current.hasNext();
					}
					return false;
				}
				return true;
			}

			@Override
			public V next() {
				return current.next();
			}

			@Override
			public void remove() {
				current.remove();
			}

		};
	}

	public Map flatten() {
		Map map = new LinkedHashMap<>();
		for (Map.Entry> entry : entrySet()) {
			List v = entry.getValue();
			if (v == null || v.isEmpty())
				continue;

			map.put(entry.getKey(), v.get(0));
		}
		return map;
	}

	public MultiMap transpose() {
		MultiMap inverted = new MultiMap<>();
		for (Map.Entry> entry : entrySet()) {
			K key = entry.getKey();

			List value = entry.getValue();
			if (value == null)
				continue;

			for (V v : value)
				inverted.add(v, key);
		}

		return inverted;
	}

	/**
	 * Return a collection with all values
	 *
	 * @return all values
	 */
	public List allValues() {
		return new IteratorList<>(all());
	}

	public static > String format(Map> map) {
		try (Formatter f = new Formatter()) {
			SortedList keys = new SortedList<>(map.keySet());
			for (Object key : keys) {
				String name = key.toString();

				SortedList values = new SortedList<>(map.get(key), null);
				String list = vertical(40, values);
				f.format("%-39s %s", name, list);
			}
			return f.toString();
		}
	}

	static String vertical(int padding, Collection used) {
		StringBuilder sb = new StringBuilder();
		String del = "";
		for (Object s : used) {
			String name = s.toString();
			sb.append(del);
			sb.append(name);
			sb.append("\r\n");
			del = pad(padding);
		}
		if (sb.length() == 0)
			sb.append("\r\n");
		return sb.toString();
	}

	static String pad(int i) {
		StringBuilder sb = new StringBuilder();
		while (i-- > 0)
			sb.append(' ');
		return sb.toString();
	}

}