package com.comphenix.protocol.wrappers.collection; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import javax.annotation.Nullable; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Represents a multimap that wraps another multimap by transforming the entries that are going in and out. * @author Kristian * * @param - the key. * @param - the inner value type. * @param - the outer value type. */ public abstract class ConvertedMultimap extends AbstractConverted implements Multimap { // Inner multimap private final Multimap inner; public ConvertedMultimap(Multimap inner) { this.inner = Preconditions.checkNotNull(inner, "inner map cannot be NULL."); } /** * Wrap a given collection. * @param inner - the inner collection. * @return The outer collection. */ protected Collection toOuterCollection(Collection inner) { return new ConvertedCollection(inner) { @Override protected VInner toInner(VOuter outer) { return ConvertedMultimap.this.toInner(outer); } @Override protected VOuter toOuter(VInner inner) { return ConvertedMultimap.this.toOuter(inner); } @Override public String toString() { return "[" + Joiner.on(", ").join(this) + "]"; } }; } /** * Wrap a given collection. * @param outer - the outer collection. * @return The inner collection. */ protected Collection toInnerCollection(Collection outer) { return new ConvertedCollection(outer) { @Override protected VOuter toInner(VInner outer) { return ConvertedMultimap.this.toOuter(outer); } @Override protected VInner toOuter(VOuter inner) { return ConvertedMultimap.this.toInner(inner); } @Override public String toString() { return "[" + Joiner.on(", ").join(this) + "]"; } }; } /** * Convert to an inner object if its of the correct type, otherwise leave it. * @param outer - the outer object. * @return The inner object, or the same object. */ @SuppressWarnings("unchecked") protected Object toInnerObject(Object outer) { return toInner((VOuter) outer); } @Override public int size() { return inner.size(); } @Override public boolean isEmpty() { return inner.isEmpty(); } @Override public boolean containsKey(@Nullable Object key) { return inner.containsKey(key); } @Override public boolean containsValue(@Nullable Object value) { return inner.containsValue(toInnerObject(value)); } @Override public boolean containsEntry(@Nullable Object key, @Nullable Object value) { return inner.containsEntry(key, toInnerObject(value)); } @Override public boolean put(@Nullable Key key, @Nullable VOuter value) { return inner.put(key, toInner(value)); } @Override public boolean remove(@Nullable Object key, @Nullable Object value) { return inner.remove(key, toInnerObject(value)); } @Override public boolean putAll(@Nullable Key key, Iterable values) { return inner.putAll(key, Iterables.transform(values, getInnerConverter())); } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public boolean putAll(Multimap multimap) { return inner.putAll(new ConvertedMultimap((Multimap) multimap) { @Override protected VOuter toInner(VInner outer) { return ConvertedMultimap.this.toOuter(outer); } @Override protected VInner toOuter(VOuter inner) { return ConvertedMultimap.this.toInner(inner); } }); } @Override public Collection replaceValues(@Nullable Key key, Iterable values) { return toOuterCollection( inner.replaceValues(key, Iterables.transform(values, getInnerConverter())) ); } @Override public Collection removeAll(@Nullable Object key) { return toOuterCollection(inner.removeAll(key)); } @Override public void clear() { inner.clear(); } @Override public Collection get(@Nullable Key key) { return toOuterCollection(inner.get(key)); } @Override public Set keySet() { return inner.keySet(); } @Override public Multiset keys() { return inner.keys(); } @Override public Collection values() { return toOuterCollection(inner.values()); } @Override public Collection> entries() { return ConvertedMap.convertedEntrySet(inner.entries(), (key, outer) -> toInner(outer), (key, inner) -> toOuter(inner)); } @Override public Map> asMap() { return new ConvertedMap, Collection>(inner.asMap()) { @Override protected Collection toInner(Collection outer) { return toInnerCollection(outer); } @Override protected Collection toOuter(Collection inner) { return toOuterCollection(inner); } }; } /** * Returns a string representation of this map. The string representation * consists of a list of key-value mappings in the order returned by the * map's entrySet view's iterator, enclosed in braces * ("{}"). Adjacent mappings are separated by the characters * ", " (comma and space). Each key-value mapping is rendered as * the key followed by an equals sign ("=") followed by the * associated value. Keys and values are converted to strings as by * {@link String#valueOf(Object)}. * * @return a string representation of this map */ public String toString() { Iterator> i = entries().iterator(); if (!i.hasNext()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); for (;;) { Entry e = i.next(); Key key = e.getKey(); VOuter value = e.getValue(); sb.append(key == this ? "(this Map)" : key); sb.append('='); sb.append(value == this ? "(this Map)" : value); if (! i.hasNext()) return sb.append('}').toString(); sb.append(", "); } } }