Added support for retrieving the signed property map of a profile.
In addition, we also allow WrappedGameProfile to be retrieved from a player.
Dieser Commit ist enthalten in:
Ursprung
ce8d115b5a
Commit
5185442f35
@ -573,6 +573,15 @@ public class MinecraftReflection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the EntityHuman class.
|
||||
* @return The entity human class.
|
||||
*/
|
||||
public static Class<?> getEntityHumanClass() {
|
||||
// Assume its the direct superclass
|
||||
return getEntityPlayerClass().getSuperclass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the GameProfile class in 1.7.2 and later.
|
||||
* @return The game profile class.
|
||||
|
@ -0,0 +1,251 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.wrappers.collection.ConvertedSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
/**
|
||||
* Represents wrappers for Minecraft's own version of Guava.
|
||||
* @author Kristian
|
||||
*/
|
||||
class GuavaWrappers {
|
||||
/**
|
||||
* Wrap a Bukkit multimap around Minecraft's internal multimap.
|
||||
* @param multimap - the multimap to wrap.
|
||||
* @return The Bukkit multimap.
|
||||
*/
|
||||
public static <TKey, TValue> Multimap<TKey, TValue> getBukkitMultimap(
|
||||
final net.minecraft.util.com.google.common.collect.Multimap<TKey, TValue> multimap) {
|
||||
return new Multimap<TKey, TValue>() {
|
||||
public Map<TKey, Collection<TValue>> asMap() {
|
||||
return multimap.asMap();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
multimap.clear();
|
||||
}
|
||||
|
||||
public boolean containsEntry(Object arg0, Object arg1) {
|
||||
return multimap.containsEntry(arg0, arg1);
|
||||
}
|
||||
|
||||
public boolean containsKey(Object arg0) {
|
||||
return multimap.containsKey(arg0);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object arg0) {
|
||||
return multimap.containsValue(arg0);
|
||||
}
|
||||
|
||||
public Collection<Entry<TKey, TValue>> entries() {
|
||||
return multimap.entries();
|
||||
}
|
||||
|
||||
public boolean equals(Object arg0) {
|
||||
return multimap.equals(arg0);
|
||||
}
|
||||
|
||||
public Collection<TValue> get(TKey arg0) {
|
||||
return multimap.get(arg0);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return multimap.hashCode();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return multimap.isEmpty();
|
||||
}
|
||||
|
||||
public Set<TKey> keySet() {
|
||||
return multimap.keySet();
|
||||
}
|
||||
|
||||
public Multiset<TKey> keys() {
|
||||
return getBukkitMultiset(multimap.keys());
|
||||
}
|
||||
|
||||
public boolean put(TKey arg0, TValue arg1) {
|
||||
return multimap.put(arg0, arg1);
|
||||
}
|
||||
|
||||
public boolean putAll(com.google.common.collect.Multimap<? extends TKey, ? extends TValue> arg0) {
|
||||
boolean result = false;
|
||||
|
||||
// Add each entry
|
||||
for (Entry<? extends TKey, ? extends TValue> entry : arg0.entries()) {
|
||||
result |= multimap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean putAll(TKey arg0, Iterable<? extends TValue> arg1) {
|
||||
return multimap.putAll(arg0, arg1);
|
||||
}
|
||||
|
||||
public boolean remove(Object arg0, Object arg1) {
|
||||
return multimap.remove(arg0, arg1);
|
||||
}
|
||||
|
||||
public Collection<TValue> removeAll(Object arg0) {
|
||||
return multimap.removeAll(arg0);
|
||||
}
|
||||
|
||||
public Collection<TValue> replaceValues(TKey arg0, Iterable<? extends TValue> arg1) {
|
||||
return multimap.replaceValues(arg0, arg1);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return multimap.size();
|
||||
}
|
||||
|
||||
public Collection<TValue> values() {
|
||||
return multimap.values();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <TValue> Multiset<TValue> getBukkitMultiset(net.minecraft.util.com.google.common.collect.Multiset<TValue> multiset2) {
|
||||
return new Multiset<TValue>() {
|
||||
private net.minecraft.util.com.google.common.collect.Multiset<TValue> multiset;
|
||||
|
||||
public int add(TValue arg0, int arg1) {
|
||||
return multiset.add(arg0, arg1);
|
||||
}
|
||||
|
||||
public boolean add(TValue arg0) {
|
||||
return multiset.add(arg0);
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends TValue> c) {
|
||||
return multiset.addAll(c);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
multiset.clear();
|
||||
}
|
||||
|
||||
public boolean contains(Object arg0) {
|
||||
return multiset.contains(arg0);
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> arg0) {
|
||||
return multiset.containsAll(arg0);
|
||||
}
|
||||
|
||||
public int count(Object arg0) {
|
||||
return multiset.count(arg0);
|
||||
}
|
||||
|
||||
public Set<TValue> elementSet() {
|
||||
return multiset.elementSet();
|
||||
}
|
||||
|
||||
public Set<Multiset.Entry<TValue>> entrySet() {
|
||||
return new ConvertedSet<
|
||||
net.minecraft.util.com.google.common.collect.Multiset.Entry<TValue>,
|
||||
Multiset.Entry<TValue>>
|
||||
(multiset.entrySet()) {
|
||||
|
||||
@Override
|
||||
protected com.google.common.collect.Multiset.Entry<TValue> toOuter(
|
||||
net.minecraft.util.com.google.common.collect.Multiset.Entry<TValue> inner) {
|
||||
return getBukkitEntry(inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected net.minecraft.util.com.google.common.collect.Multiset.Entry<TValue> toInner(
|
||||
com.google.common.collect.Multiset.Entry<TValue> outer) {
|
||||
throw new UnsupportedOperationException("Cannot convert " + outer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean equals(Object arg0) {
|
||||
return multiset.equals(arg0);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return multiset.hashCode();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return multiset.isEmpty();
|
||||
}
|
||||
|
||||
public Iterator<TValue> iterator() {
|
||||
return multiset.iterator();
|
||||
}
|
||||
|
||||
public int remove(Object arg0, int arg1) {
|
||||
return multiset.remove(arg0, arg1);
|
||||
}
|
||||
|
||||
public boolean remove(Object arg0) {
|
||||
return multiset.remove(arg0);
|
||||
}
|
||||
|
||||
public boolean removeAll(Collection<?> arg0) {
|
||||
return multiset.removeAll(arg0);
|
||||
}
|
||||
|
||||
public boolean retainAll(Collection<?> arg0) {
|
||||
return multiset.retainAll(arg0);
|
||||
}
|
||||
|
||||
public boolean setCount(TValue arg0, int arg1, int arg2) {
|
||||
return multiset.setCount(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
public int setCount(TValue arg0, int arg1) {
|
||||
return multiset.setCount(arg0, arg1);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return multiset.size();
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return multiset.toArray();
|
||||
}
|
||||
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return multiset.toArray(a);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return multiset.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static <TValue> Multiset.Entry<TValue> getBukkitEntry(final net.minecraft.util.com.google.common.collect.Multiset.Entry<TValue> entry) {
|
||||
return new Multiset.Entry<TValue>() {
|
||||
public boolean equals(Object arg0) {
|
||||
return entry.equals(arg0);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return entry.getCount();
|
||||
}
|
||||
|
||||
public TValue getElement() {
|
||||
return entry.getElement();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return entry.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return entry.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -3,13 +3,19 @@ package com.comphenix.protocol.wrappers;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.collection.ConvertedMultimap;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import net.minecraft.util.com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.util.com.mojang.authlib.properties.Property;
|
||||
|
||||
/**
|
||||
* Represents a wrapper for a game profile.
|
||||
@ -20,12 +26,34 @@ public class WrappedGameProfile extends AbstractWrapper {
|
||||
private static final ConstructorAccessor CREATE_STRING_STRING = Accessors.getConstructorAccessorOrNull(GameProfile.class, String.class, String.class);
|
||||
private static final FieldAccessor GET_UUID_STRING = Accessors.getFieldAcccessorOrNull(GameProfile.class, "id", String.class);
|
||||
|
||||
// Fetching game profile
|
||||
private static FieldAccessor GET_PROFILE;
|
||||
|
||||
// Property map
|
||||
private Multimap<String, WrappedSignedProperty> propertyMap;
|
||||
|
||||
// Profile from a handle
|
||||
private WrappedGameProfile(Object profile) {
|
||||
super(GameProfile.class);
|
||||
setHandle(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated game profile of a player.
|
||||
* @param player - the player.
|
||||
* @return The game profile.
|
||||
*/
|
||||
public static WrappedGameProfile fromPlayer(Player player) {
|
||||
FieldAccessor accessor = GET_PROFILE;
|
||||
Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
|
||||
|
||||
if (accessor == null) {
|
||||
accessor = Accessors.getFieldAccessor(MinecraftReflection.getEntityHumanClass(), GameProfile.class, true);
|
||||
GET_PROFILE = accessor;
|
||||
}
|
||||
return WrappedGameProfile.fromHandle(GET_PROFILE.get(nmsPlayer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new game profile with the given properties.
|
||||
* <p>
|
||||
@ -110,6 +138,39 @@ public class WrappedGameProfile extends AbstractWrapper {
|
||||
return getProfile().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the property map of signed values.
|
||||
* @return Property map.
|
||||
*/
|
||||
public Multimap<String, WrappedSignedProperty> getProperties() {
|
||||
Multimap<String, WrappedSignedProperty> result = propertyMap;
|
||||
|
||||
if (result == null) {
|
||||
result = new ConvertedMultimap<String, Property, WrappedSignedProperty>(
|
||||
GuavaWrappers.getBukkitMultimap(getProfile().getProperties())) {
|
||||
@Override
|
||||
protected Property toInner(WrappedSignedProperty outer) {
|
||||
return (Property) outer.handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object toInnerObject(Object outer) {
|
||||
if (outer instanceof WrappedSignedProperty) {
|
||||
return toInner((WrappedSignedProperty) outer);
|
||||
}
|
||||
return outer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WrappedSignedProperty toOuter(Property inner) {
|
||||
return WrappedSignedProperty.fromHandle(inner);
|
||||
}
|
||||
};
|
||||
propertyMap = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying GameProfile.
|
||||
* @return The GameProfile.
|
||||
|
@ -0,0 +1,106 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import net.minecraft.util.com.mojang.authlib.properties.Property;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Represents a wrapper over a signed property.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class WrappedSignedProperty extends AbstractWrapper {
|
||||
/**
|
||||
* Construct a new wrapped signed property from a given handle.
|
||||
* @param handle - the handle.
|
||||
*/
|
||||
private WrappedSignedProperty(Object handle) {
|
||||
super(Property.class);
|
||||
setHandle(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new signed property from a given NMS property.
|
||||
* @param handle - the property.
|
||||
* @return The wrapped signed property.
|
||||
*/
|
||||
public static WrappedSignedProperty fromHandle(Object handle) {
|
||||
return new WrappedSignedProperty(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying signed property.
|
||||
* @return The GameProfile.
|
||||
*/
|
||||
private Property getProfile() {
|
||||
return (Property) handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the underlying property, such as "textures".
|
||||
* @return Name of the property.
|
||||
*/
|
||||
public String getName() {
|
||||
return getProfile().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the signature of the property (base64) as returned by the session server's /hasJoined.
|
||||
* @return The signature of the property.
|
||||
*/
|
||||
public String getSignature() {
|
||||
return getProfile().getSignature();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value of the property (base64) as return by the session server's /hasJoined
|
||||
* @return The value of the property.
|
||||
*/
|
||||
public String getValue() {
|
||||
return getProfile().getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this property has a signature.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasSignature() {
|
||||
return getProfile().hasSignature();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the signature of this property is valid and signed by the corresponding private key.
|
||||
* @param key - the public key.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isSignatureValid(PublicKey key) {
|
||||
return getProfile().isSignatureValid(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(getName(), getValue(), getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object){
|
||||
if (object instanceof WrappedSignedProperty) {
|
||||
if (!super.equals(object))
|
||||
return false;
|
||||
WrappedSignedProperty that = (WrappedSignedProperty) object;
|
||||
return Objects.equal(this.getName(), that.getName())
|
||||
&& Objects.equal(this.getValue(), that.getValue())
|
||||
&& Objects.equal(this.getSignature(), that.getSignature());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toStringHelper(this)
|
||||
.add("name", getName())
|
||||
.add("value", getValue())
|
||||
.add("signature", getSignature())
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -30,6 +30,22 @@ import com.google.common.base.Function;
|
||||
* @param <VOuter> - the second type.
|
||||
*/
|
||||
public abstract class AbstractConverted<VInner, VOuter> {
|
||||
// Inner conversion
|
||||
private Function<VOuter, VInner> innerConverter = new Function<VOuter, VInner>() {
|
||||
@Override
|
||||
public VInner apply(@Nullable VOuter param) {
|
||||
return toInner(param);
|
||||
}
|
||||
};
|
||||
|
||||
// Outer conversion
|
||||
private Function<VInner, VOuter> outerConverter = new Function<VInner, VOuter>() {
|
||||
@Override
|
||||
public VOuter apply(@Nullable VInner param) {
|
||||
return toOuter(param);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a value from the inner map to the outer visible map.
|
||||
* @param inner - the inner value.
|
||||
@ -44,16 +60,19 @@ public abstract class AbstractConverted<VInner, VOuter> {
|
||||
*/
|
||||
protected abstract VInner toInner(VOuter outer);
|
||||
|
||||
/**
|
||||
* Retrieve a function delegate that converts outer objects to inner objects.
|
||||
* @return A function delegate.
|
||||
*/
|
||||
protected Function<VOuter, VInner> getInnerConverter() {
|
||||
return innerConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a function delegate that converts inner objects to outer objects.
|
||||
* @return A function delegate.
|
||||
*/
|
||||
protected Function<VInner, VOuter> getOuterConverter() {
|
||||
return new Function<VInner, VOuter>() {
|
||||
@Override
|
||||
public VOuter apply(@Nullable VInner param) {
|
||||
return toOuter(param);
|
||||
}
|
||||
};
|
||||
return outerConverter;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.comphenix.protocol.wrappers.collection;
|
||||
|
||||
/**
|
||||
* Represents a function that accepts two parameters.
|
||||
* @author Kristian
|
||||
* @param <T1> - type of the first parameter.
|
||||
* @param <T2> - type of the second parameter.
|
||||
* @param <TResult> - type of the return value.
|
||||
*/
|
||||
public interface BiFunction<T1, T2, TResult> {
|
||||
public TResult apply(T1 arg1, T2 arg2);
|
||||
}
|
@ -39,6 +39,22 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
||||
// Inner map
|
||||
private Map<Key, VInner> inner;
|
||||
|
||||
// Inner conversion
|
||||
private BiFunction<Key, VOuter, VInner> innerConverter = new BiFunction<Key, VOuter, VInner>() {
|
||||
@Override
|
||||
public VInner apply(Key key, VOuter outer) {
|
||||
return toInner(key, outer);
|
||||
}
|
||||
};
|
||||
|
||||
// Outer conversion
|
||||
private BiFunction<Key, VInner, VOuter> outerConverter = new BiFunction<Key, VInner, VOuter>() {
|
||||
@Override
|
||||
public VOuter apply(Key key, VInner inner) {
|
||||
return toOuter(key, inner);
|
||||
}
|
||||
};
|
||||
|
||||
public ConvertedMap(Map<Key, VInner> inner) {
|
||||
if (inner == null)
|
||||
throw new IllegalArgumentException("Inner map cannot be NULL.");
|
||||
@ -63,58 +79,7 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
||||
|
||||
@Override
|
||||
public Set<Entry<Key, VOuter>> entrySet() {
|
||||
return new ConvertedSet<Entry<Key,VInner>, Entry<Key,VOuter>>(inner.entrySet()) {
|
||||
@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 ConvertedMap.this.toInner(outer.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VInner setValue(VInner value) {
|
||||
return ConvertedMap.this.toInner(outer.setValue(ConvertedMap.this.toOuter(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 ConvertedMap.this.toOuter(inner.getKey(), inner.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VOuter setValue(VOuter value) {
|
||||
final VInner converted = ConvertedMap.this.toInner(getKey(), value);
|
||||
return ConvertedMap.this.toOuter(getKey(), inner.setValue(converted));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("\"%s\": %s", getKey(), getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
return convertedEntrySet(inner.entrySet(), innerConverter, outerConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,4 +180,70 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
|
||||
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());
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,246 @@
|
||||
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(),
|
||||
new BiFunction<Key, VOuter, VInner>() {
|
||||
public VInner apply(Key key, VOuter outer) {
|
||||
return toInner(outer);
|
||||
}
|
||||
},
|
||||
new BiFunction<Key, VInner, VOuter>() {
|
||||
public VOuter apply(Key key, VInner inner) {
|
||||
return 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 <tt>entrySet</tt> view's iterator, enclosed in braces
|
||||
* (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
|
||||
* <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
|
||||
* the key followed by an equals sign (<tt>"="</tt>) 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(", ");
|
||||
}
|
||||
}
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren