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

235 lines
6.7 KiB
Java

/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers.collection;
import com.google.common.collect.Collections2;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Represents a map that wraps another map by transforming the entries going in and out.
*
* @author Kristian
*
* @param <VInner> - type of the value in the entries in the inner invisible map.
* @param <VOuter> - type of the value in the entries publically accessible in the outer map.
*/
public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverted<VInner, VOuter> implements Map<Key, VOuter> {
// Inner map
private Map<Key, VInner> inner;
// Inner conversion
private final BiFunction<Key, VOuter, VInner> innerConverter = this::toInner;
// Outer conversion
private final BiFunction<Key, VInner, VOuter> outerConverter = this::toOuter;
public ConvertedMap(Map<Key, VInner> inner) {
if (inner == null)
throw new IllegalArgumentException("Inner map cannot be NULL.");
this.inner = inner;
}
@Override
public void clear() {
inner.clear();
}
@Override
public boolean containsKey(Object key) {
return inner.containsKey(key);
}
@Override
@SuppressWarnings("unchecked")
public boolean containsValue(Object value) {
return inner.containsValue(toInner((VOuter) value));
}
@Override
public Set<Entry<Key, VOuter>> entrySet() {
return convertedEntrySet(inner.entrySet(), innerConverter, outerConverter);
}
/**
* Convert a value from the inner map to the outer visible map.
* @param key - unused value.
* @param inner - the inner value.
* @return The outer value.
*/
protected VOuter toOuter(Key key, VInner inner) {
return toOuter(inner);
}
/**
* Convert a value from the outer map to the internal inner map.
* @param key - unused value.
* @param outer - the outer value.
* @return The inner value.
*/
protected VInner toInner(Key key, VOuter outer) {
return toInner(outer);
}
@SuppressWarnings("unchecked")
@Override
public VOuter get(Object key) {
return toOuter((Key) key, inner.get(key));
}
@Override
public boolean isEmpty() {
return inner.isEmpty();
}
@Override
public Set<Key> keySet() {
return inner.keySet();
}
@Override
public VOuter put(Key key, VOuter value) {
return toOuter(key, inner.put(key, toInner(key, value)));
}
@Override
public void putAll(Map<? extends Key, ? extends VOuter> m) {
for (Entry<? extends Key, ? extends VOuter> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked")
@Override
public VOuter remove(Object key) {
return toOuter((Key) key, inner.remove(key));
}
@Override
public int size() {
return inner.size();
}
@Override
public Collection<VOuter> values() {
return Collections2.transform(entrySet(), Entry::getValue);
}
/**
* 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
*/
@Override
public String toString() {
Iterator<Entry<Key, VOuter>> i = entrySet().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(", ");
}
}
/**
* Convert a collection of entries.
* @param entries - the collection of entries.
* @param innerFunction - the inner entry converter.
* @param outerFunction - the outer entry converter.
* @return The converted set of entries.
*/
static <Key, VInner, VOuter> Set<Entry<Key, VOuter>> convertedEntrySet(
final Collection<Entry<Key, VInner>> entries,
final BiFunction<Key, VOuter, VInner> innerFunction,
final BiFunction<Key, VInner, VOuter> outerFunction) {
return new ConvertedSet<Entry<Key,VInner>, Entry<Key,VOuter>>(entries) {
@Override
protected Entry<Key, VInner> toInner(final Entry<Key, VOuter> outer) {
return new Entry<Key, VInner>() {
@Override
public Key getKey() {
return outer.getKey();
}
@Override
public VInner getValue() {
return innerFunction.apply(getKey(), outer.getValue());
}
@Override
public VInner setValue(VInner value) {
return innerFunction.apply(getKey(), outer.setValue(outerFunction.apply(getKey(), value)));
}
@Override
public String toString() {
return String.format("\"%s\": %s", getKey(), getValue());
}
};
}
@Override
protected Entry<Key, VOuter> toOuter(final Entry<Key, VInner> inner) {
return new Entry<Key, VOuter>() {
@Override
public Key getKey() {
return inner.getKey();
}
@Override
public VOuter getValue() {
return outerFunction.apply(getKey(), inner.getValue());
}
@Override
public VOuter setValue(VOuter value) {
final VInner converted = innerFunction.apply(getKey(), value);
return outerFunction.apply(getKey(), inner.setValue(converted));
}
@Override
public String toString() {
return String.format("\"%s\": %s", getKey(), getValue());
}
};
}
};
}
}