mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 19:46:33 +01:00
Adding support for Spigot MCPC 1.2.5.
Very buggy indeed.
This commit is contained in:
parent
505226f8ad
commit
82bb7a7c43
@ -270,7 +270,7 @@ public class CommandFilter extends CommandBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Description: Adds or removes a simple packet listener.
|
* Description: Adds or removes a simple packet filter.
|
||||||
Usage: /<command> add|remove name [packet IDs]
|
Usage: /<command> add|remove name [packet IDs]
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -340,6 +340,8 @@ class CommandPacket extends CommandBase {
|
|||||||
supported.addAll(Packets.Client.getSupported());
|
supported.addAll(Packets.Client.getSupported());
|
||||||
else if (side.isForServer())
|
else if (side.isForServer())
|
||||||
supported.addAll(Packets.Server.getSupported());
|
supported.addAll(Packets.Server.getSupported());
|
||||||
|
|
||||||
|
System.out.println("Supported for " + side + ": " + supported);
|
||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
reporter.reportWarning(this, "Cannot load server and client packet list.", e);
|
reporter.reportWarning(this, "Cannot load server and client packet list.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
} catch (FieldAccessException e) {
|
||||||
reporter.reportWarning(this, "Unable to initialize packet injector.", e);
|
reporter.reportWarning(this, "Unable to initialize packet injector.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -757,7 +757,14 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
if (!MinecraftReflection.isPacketClass(packet))
|
if (!MinecraftReflection.isPacketClass(packet))
|
||||||
throw new IllegalArgumentException("The given object " + packet + " is not a packet.");
|
throw new IllegalArgumentException("The given object " + packet + " is not a packet.");
|
||||||
|
|
||||||
return PacketRegistry.getPacketToID().get(packet.getClass());
|
Integer id = PacketRegistry.getPacketToID().get(packet.getClass());
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
return id;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unable to find associated packet of " + packet + ": Lookup returned NULL.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,6 +8,7 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,9 +101,9 @@ public class PacketInjectorBuilder {
|
|||||||
* <p>
|
* <p>
|
||||||
* Note that any non-null builder parameters must be set.
|
* Note that any non-null builder parameters must be set.
|
||||||
* @return The created injector.
|
* @return The created injector.
|
||||||
* @throws IllegalAccessException If anything goes wrong in terms of reflection.
|
* @throws FieldAccessException If anything goes wrong in terms of reflection.
|
||||||
*/
|
*/
|
||||||
public PacketInjector buildInjector() throws IllegalAccessException {
|
public PacketInjector buildInjector() throws FieldAccessException {
|
||||||
initializeDefaults();
|
initializeDefaults();
|
||||||
return new ProxyPacketInjector(classLoader, invoker, playerInjection, reporter);
|
return new ProxyPacketInjector(classLoader, invoker, playerInjection, reporter);
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,15 @@ import java.util.Set;
|
|||||||
|
|
||||||
import net.sf.cglib.proxy.Factory;
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.wrappers.TroveWrapper;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
@ -39,6 +44,8 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public class PacketRegistry {
|
public class PacketRegistry {
|
||||||
|
private static final int MIN_SERVER_PACKETS = 5;
|
||||||
|
private static final int MIN_CLIENT_PACKETS = 5;
|
||||||
|
|
||||||
// Fuzzy reflection
|
// Fuzzy reflection
|
||||||
private static FuzzyReflection packetRegistry;
|
private static FuzzyReflection packetRegistry;
|
||||||
@ -67,6 +74,14 @@ public class PacketRegistry {
|
|||||||
try {
|
try {
|
||||||
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
|
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
|
||||||
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Spigot 1.2.5 MCPC workaround
|
||||||
|
try {
|
||||||
|
packetToID = getSpigotWrapper();
|
||||||
|
} catch (Exception e2) {
|
||||||
|
// Very bad indeed
|
||||||
|
throw new IllegalArgumentException(e.getMessage() + "; Spigot workaround failed.", e2);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
||||||
@ -76,6 +91,40 @@ public class PacketRegistry {
|
|||||||
return packetToID;
|
return packetToID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<Class, Integer> getSpigotWrapper() throws IllegalAccessException {
|
||||||
|
// If it talks like a duck, etc.
|
||||||
|
// Perhaps it would be nice to have a proper duck typing library as well
|
||||||
|
FuzzyClassContract mapLike = FuzzyClassContract.newBuilder().
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact("size").returnTypeExact(int.class)).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact("put").parameterCount(2)).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact("get").parameterCount(1)).
|
||||||
|
build();
|
||||||
|
|
||||||
|
Field packetsField = getPacketRegistry().getField(
|
||||||
|
FuzzyFieldContract.newBuilder().typeMatches(mapLike).build());
|
||||||
|
Object troveMap = FieldUtils.readStaticField(packetsField, true);
|
||||||
|
|
||||||
|
// Check for stupid no_entry_values
|
||||||
|
try {
|
||||||
|
Field field = FieldUtils.getField(troveMap.getClass(), "no_entry_value", true);
|
||||||
|
Integer value = (Integer) FieldUtils.readField(field, troveMap, true);
|
||||||
|
|
||||||
|
if (value >= 0 && value < 256) {
|
||||||
|
// Someone forgot to set the no entry value. Let's help them.
|
||||||
|
FieldUtils.writeField(field, troveMap, -1);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Whatever
|
||||||
|
ProtocolLibrary.getErrorReporter().reportWarning(PacketRegistry.class, "Unable to correct no entry value.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll assume this a Trove map
|
||||||
|
return TroveWrapper.getDecoratedMap(troveMap);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
|
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
|
||||||
* @return Reflected packet registry.
|
* @return Reflected packet registry.
|
||||||
@ -109,6 +158,10 @@ public class PacketRegistry {
|
|||||||
*/
|
*/
|
||||||
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
||||||
initializeSets();
|
initializeSets();
|
||||||
|
|
||||||
|
// Sanity check. This is impossible!
|
||||||
|
if (serverPackets != null && serverPackets.size() < MIN_SERVER_PACKETS)
|
||||||
|
throw new FieldAccessException("Server packet list is empty. Seems to be unsupported");
|
||||||
return serverPackets;
|
return serverPackets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +172,10 @@ public class PacketRegistry {
|
|||||||
*/
|
*/
|
||||||
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
||||||
initializeSets();
|
initializeSets();
|
||||||
|
|
||||||
|
// As above
|
||||||
|
if (clientPackets != null && clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||||
|
throw new FieldAccessException("Client packet list is empty. Seems to be unsupported");
|
||||||
return clientPackets;
|
return clientPackets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +197,14 @@ public class PacketRegistry {
|
|||||||
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
||||||
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
||||||
|
|
||||||
|
// Check sizes
|
||||||
|
if (serverPackets.size() < MIN_SERVER_PACKETS)
|
||||||
|
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||||
|
PacketRegistry.class, "Too few server packets detected: " + serverPackets.size());
|
||||||
|
if (clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||||
|
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||||
|
PacketRegistry.class, "Too few client packets detected: " + clientPackets.size());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
|
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import com.comphenix.protocol.events.PacketContainer;
|
|||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.MethodInfo;
|
import com.comphenix.protocol.reflect.MethodInfo;
|
||||||
@ -49,6 +50,85 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class ProxyPacketInjector implements PacketInjector {
|
class ProxyPacketInjector implements PacketInjector {
|
||||||
|
/**
|
||||||
|
* Represents a way to update the packet ID to class lookup table.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private static interface PacketClassLookup {
|
||||||
|
public void setLookup(int packetID, Class<?> clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class IntHashMapLookup implements PacketClassLookup {
|
||||||
|
// The "put" method that associates a packet ID with a packet class
|
||||||
|
private Method putMethod;
|
||||||
|
private Object intHashMap;
|
||||||
|
|
||||||
|
public IntHashMapLookup() throws IllegalAccessException {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLookup(int packetID, Class<?> clazz) {
|
||||||
|
try {
|
||||||
|
putMethod.invoke(intHashMap, packetID, clazz);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException("Illegal argument.", e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot access method.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() throws IllegalAccessException {
|
||||||
|
if (intHashMap == null) {
|
||||||
|
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
|
||||||
|
Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
|
||||||
|
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
|
||||||
|
|
||||||
|
try {
|
||||||
|
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException("Minecraft is incompatible.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, get the "put" method.
|
||||||
|
putMethod = FuzzyReflection.fromObject(intHashMap).
|
||||||
|
getMethodByParameters("put", int.class, Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ArrayLookup implements PacketClassLookup {
|
||||||
|
private Class<?>[] array;
|
||||||
|
|
||||||
|
public ArrayLookup() throws IllegalAccessException {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLookup(int packetID, Class<?> clazz) {
|
||||||
|
array[packetID] = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() throws IllegalAccessException {
|
||||||
|
FuzzyReflection reflection = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass());
|
||||||
|
|
||||||
|
// Is there a Class array with 256 elements instead?
|
||||||
|
for (Field field : reflection.getFieldListByType(Class[].class)) {
|
||||||
|
Class<?>[] test = (Class<?>[]) FieldUtils.readField(field, (Object)null);
|
||||||
|
|
||||||
|
if (test.length == 256) {
|
||||||
|
array = test;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unable to find an array with the type " + Class[].class +
|
||||||
|
" in " + MinecraftReflection.getPacketClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matches the readPacketData(DataInputStream) method in Packet.
|
* Matches the readPacketData(DataInputStream) method in Packet.
|
||||||
*/
|
*/
|
||||||
@ -58,9 +138,7 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
parameterCount(1).
|
parameterCount(1).
|
||||||
build();
|
build();
|
||||||
|
|
||||||
// The "put" method that associates a packet ID with a packet class
|
private static PacketClassLookup lookup;
|
||||||
private static Method putMethod;
|
|
||||||
private static Object intHashMap;
|
|
||||||
|
|
||||||
// The packet filter manager
|
// The packet filter manager
|
||||||
private ListenerInvoker manager;
|
private ListenerInvoker manager;
|
||||||
@ -78,7 +156,7 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
private CallbackFilter filter;
|
private CallbackFilter filter;
|
||||||
|
|
||||||
public ProxyPacketInjector(ClassLoader classLoader, ListenerInvoker manager,
|
public ProxyPacketInjector(ClassLoader classLoader, ListenerInvoker manager,
|
||||||
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws IllegalAccessException {
|
PlayerInjectionHandler playerInjection, ErrorReporter reporter) throws FieldAccessException {
|
||||||
|
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@ -100,20 +178,21 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialize() throws IllegalAccessException {
|
private void initialize() throws FieldAccessException {
|
||||||
if (intHashMap == null) {
|
if (lookup == null) {
|
||||||
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
|
try {
|
||||||
Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
|
lookup = new IntHashMapLookup();
|
||||||
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
|
} catch (Exception e1) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true);
|
lookup = new ArrayLookup();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (Exception e2) {
|
||||||
throw new RuntimeException("Minecraft is incompatible.", e);
|
// Wow
|
||||||
|
throw new FieldAccessException(e1.getMessage() + ". Workaround failed too.", e2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, get the "put" method.
|
// Should work fine now
|
||||||
putMethod = FuzzyReflection.fromObject(intHashMap).getMethodByParameters("put", int.class, Object.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,21 +252,12 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
// Add a static reference
|
// Add a static reference
|
||||||
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
|
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
|
||||||
|
|
||||||
try {
|
|
||||||
// Override values
|
// Override values
|
||||||
previous.put(packetID, old);
|
previous.put(packetID, old);
|
||||||
registry.put(proxy, packetID);
|
registry.put(proxy, packetID);
|
||||||
overwritten.put(packetID, proxy);
|
overwritten.put(packetID, proxy);
|
||||||
putMethod.invoke(intHashMap, packetID, proxy);
|
lookup.setLookup(packetID, proxy);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new RuntimeException("Illegal argument.", e);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException("Cannot access method.", e);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -200,25 +270,14 @@ class ProxyPacketInjector implements PacketInjector {
|
|||||||
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
|
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
|
||||||
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
|
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
|
||||||
|
|
||||||
// Use the old class definition
|
|
||||||
try {
|
|
||||||
Class old = previous.get(packetID);
|
Class old = previous.get(packetID);
|
||||||
Class proxy = PacketRegistry.getPacketClassFromID(packetID);
|
Class proxy = PacketRegistry.getPacketClassFromID(packetID);
|
||||||
|
|
||||||
putMethod.invoke(intHashMap, packetID, old);
|
lookup.setLookup(packetID, old);
|
||||||
previous.remove(packetID);
|
previous.remove(packetID);
|
||||||
registry.remove(proxy);
|
registry.remove(proxy);
|
||||||
overwritten.remove(packetID);
|
overwritten.remove(packetID);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Handle some problems
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return false;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException("Cannot access method.", e);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
package com.comphenix.protocol.wrappers;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap a GNU Trove Collection class with an equivalent Java Collection class.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class TroveWrapper {
|
||||||
|
private volatile static Class<?> decorators;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a Java wrapper for the corresponding Trove map.
|
||||||
|
* @param troveMap - the trove map to wrap.
|
||||||
|
* @return The wrapped GNU Trove map.
|
||||||
|
* @throws IllegalStateException If GNU Trove cannot be found in the class map.
|
||||||
|
* @throws IllegalArgumentException If troveMap is NULL.
|
||||||
|
* @throws FieldAccessException Error in wrapper method or lack of reflection permissions.
|
||||||
|
*/
|
||||||
|
public static <TKey, TValue> Map<TKey, TValue> getDecoratedMap(@Nonnull Object troveMap) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<TKey, TValue> result = (Map<TKey, TValue>) getDecorated(troveMap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a Java wrapper for the corresponding Trove set.
|
||||||
|
* @param troveSet - the trove set to wrap.
|
||||||
|
* @return The wrapped GNU Trove set.
|
||||||
|
* @throws IllegalStateException If GNU Trove cannot be found in the class map.
|
||||||
|
* @throws IllegalArgumentException If troveSet is NULL.
|
||||||
|
* @throws FieldAccessException Error in wrapper method or lack of reflection permissions.
|
||||||
|
*/
|
||||||
|
public static <TValue> Set<TValue> getDecoratedSet(@Nonnull Object troveSet) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Set<TValue> result = (Set<TValue>) getDecorated(troveSet);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a Java wrapper for the corresponding Trove list.
|
||||||
|
* @param troveList - the trove list to wrap.
|
||||||
|
* @return The wrapped GNU Trove list.
|
||||||
|
* @throws IllegalStateException If GNU Trove cannot be found in the class map.
|
||||||
|
* @throws IllegalArgumentException If troveList is NULL.
|
||||||
|
* @throws FieldAccessException Error in wrapper method or lack of reflection permissions.
|
||||||
|
*/
|
||||||
|
public static <TValue> List<TValue> getDecoratedList(@Nonnull Object troveList) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<TValue> result = (List<TValue>) getDecorated(troveList);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getDecorated(@Nonnull Object trove) {
|
||||||
|
if (trove == null)
|
||||||
|
throw new IllegalArgumentException("trove instance cannot be non-null.");
|
||||||
|
|
||||||
|
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
|
||||||
|
|
||||||
|
if (decorators == null) {
|
||||||
|
try {
|
||||||
|
// Attempt to get decorator class
|
||||||
|
decorators = TroveWrapper.class.getClassLoader().loadClass("gnu.trove.TDecorators");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new IllegalStateException("Cannot find TDecorators in Gnu Trove.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find an appropriate wrapper method in TDecorators
|
||||||
|
for (Method method : decorators.getMethods()) {
|
||||||
|
Class<?>[] types = method.getParameterTypes();
|
||||||
|
|
||||||
|
if (types.length == 1 && match.isMatch(types[0], null)) {
|
||||||
|
try {
|
||||||
|
Object result = method.invoke(null, trove);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
throw new FieldAccessException("Wrapper returned NULL.");
|
||||||
|
else
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new FieldAccessException("Cannot invoke wrapper method.", e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new FieldAccessException("Illegal access.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new FieldAccessException("Error in invocation.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Cannot find decorator for " + trove + " (" + trove.getClass() + ")");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user