ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/collection/ConvertedMultimap.java

236 lines
6.4 KiB
Java

package com.comphenix.protocol.wrappers.collection;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
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;
/**
* Represents a multimap that wraps another multimap by transforming the entries that are going in and out.
* @author Kristian
*
* @param <Key> - the key.
* @param <VInner> - the inner value type.
* @param <VOuter> - the outer value type.
*/
public abstract class ConvertedMultimap<Key, VInner, VOuter> extends AbstractConverted<VInner, VOuter> implements Multimap<Key, VOuter> {
// Inner multimap
private Multimap<Key, VInner> inner;
public ConvertedMultimap(Multimap<Key, VInner> 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<VOuter> toOuterCollection(Collection<VInner> inner) {
return new ConvertedCollection<VInner, VOuter>(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<VInner> toInnerCollection(Collection<VOuter> outer) {
return new ConvertedCollection<VOuter, VInner>(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<? extends VOuter> values) {
return inner.putAll(key, Iterables.transform(values, getInnerConverter()));
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public boolean putAll(Multimap<? extends Key, ? extends VOuter> multimap) {
return inner.putAll(new ConvertedMultimap<Key, VOuter, VInner>((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<VOuter> replaceValues(@Nullable Key key, Iterable<? extends VOuter> values) {
return toOuterCollection(
inner.replaceValues(key, Iterables.transform(values, getInnerConverter()))
);
}
@Override
public Collection<VOuter> removeAll(@Nullable Object key) {
return toOuterCollection(inner.removeAll(key));
}
@Override
public void clear() {
inner.clear();
}
@Override
public Collection<VOuter> get(@Nullable Key key) {
return toOuterCollection(inner.get(key));
}
@Override
public Set<Key> keySet() {
return inner.keySet();
}
@Override
public Multiset<Key> keys() {
return inner.keys();
}
@Override
public Collection<VOuter> values() {
return toOuterCollection(inner.values());
}
@Override
public Collection<Entry<Key, VOuter>> entries() {
return ConvertedMap.convertedEntrySet(inner.entries(), (key, outer) -> toInner(outer), (key, inner) -> toOuter(inner));
}
@Override
public Map<Key, Collection<VOuter>> asMap() {
return new ConvertedMap<Key, Collection<VInner>, Collection<VOuter>>(inner.asMap()) {
@Override
protected Collection<VInner> toInner(Collection<VOuter> outer) {
return toInnerCollection(outer);
}
@Override
protected Collection<VOuter> toOuter(Collection<VInner> 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 <code>entrySet</code> view's iterator, enclosed in braces
* (<code>"{}"</code>). Adjacent mappings are separated by the characters
* <code>", "</code> (comma and space). Each key-value mapping is rendered as
* the key followed by an equals sign (<code>"="</code>) 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<Entry<Key, VOuter>> i = entries().iterator();
if (!i.hasNext())
return "{}";
StringBuilder sb = new StringBuilder();
sb.append('{');
for (;;) {
Entry<Key, VOuter> 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(", ");
}
}
}