From 035277bdeb98ace32e20504a16578e36f3c17dd0 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 13 Sep 2012 13:57:22 +0200 Subject: [PATCH] Added support for at least Beta 1.8. This involved a bunch of reflection magic, along with the removal of every Apache Commons reference, in addition to every internal Guava class. --- .../comphenix/protocol/ProtocolManager.java | 15 +- .../protocol/events/PacketAdapter.java | 29 +++- .../protocol/events/PacketContainer.java | 3 +- .../protocol/injector/MinecraftRegistry.java | 5 +- .../injector/PacketFilterManager.java | 139 +++++++++++++++--- .../protocol/injector/PacketInjector.java | 5 +- .../protocol/injector/ReadPacketModifier.java | 5 +- .../protocol/reflect/DefaultInstances.java | 21 ++- .../protocol/reflect/FieldUtils.java | 46 +++++- .../protocol/reflect/FuzzyReflection.java | 5 +- .../protocol/reflect/PrimitiveUtils.java | 124 ++++++++++++++++ .../protocol/reflect/StructureModifier.java | 3 +- 12 files changed, 344 insertions(+), 56 deletions(-) create mode 100644 ProtocolLib/src/com/comphenix/protocol/reflect/PrimitiveUtils.java diff --git a/ProtocolLib/src/com/comphenix/protocol/ProtocolManager.java b/ProtocolLib/src/com/comphenix/protocol/ProtocolManager.java index cd0272b5..297042a4 100644 --- a/ProtocolLib/src/com/comphenix/protocol/ProtocolManager.java +++ b/ProtocolLib/src/com/comphenix/protocol/ProtocolManager.java @@ -4,6 +4,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.Set; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketListener; @@ -19,20 +20,28 @@ public interface ProtocolManager { * Retrieves a list of every registered packet listener. * @return Every registered packet listener. */ - public abstract ImmutableSet getPacketListeners(); + public ImmutableSet getPacketListeners(); /** * Adds a packet listener. * @param listener - new packet listener. */ - public abstract void addPacketListener(PacketListener listener); + public void addPacketListener(PacketListener listener); /** * Removes a given packet listener. * @param listener - the packet listener to remove. */ - public abstract void removePacketListener(PacketListener listener); + public void removePacketListener(PacketListener listener); + /** + * Removes every listener associated with the given plugin. + *

+ * Note that this only works for listeners that derive from PacketAdapter. + * @param plugin - the plugin to unload. + */ + public void removePacketAdapters(Plugin plugin); + /** * Send a packet to the given player. * @param reciever - the reciever. diff --git a/ProtocolLib/src/com/comphenix/protocol/events/PacketAdapter.java b/ProtocolLib/src/com/comphenix/protocol/events/PacketAdapter.java index 16a11659..2244301b 100644 --- a/ProtocolLib/src/com/comphenix/protocol/events/PacketAdapter.java +++ b/ProtocolLib/src/com/comphenix/protocol/events/PacketAdapter.java @@ -2,9 +2,9 @@ package com.comphenix.protocol.events; import java.util.Set; -import org.apache.commons.lang.StringUtils; -import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.Plugin; +import com.google.common.base.Joiner; import com.google.common.collect.Sets; /** @@ -14,7 +14,7 @@ import com.google.common.collect.Sets; */ public abstract class PacketAdapter implements PacketListener { - protected JavaPlugin plugin; + protected Plugin plugin; protected Set packetsID; protected ConnectionSide connectionSide; @@ -24,7 +24,7 @@ public abstract class PacketAdapter implements PacketListener { * @param connectionSide - the packet type the listener is looking for. * @param packets - the packet IDs the listener is looking for. */ - public PacketAdapter(JavaPlugin plugin, ConnectionSide connectionSide, Integer... packets) { + public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, Integer... packets) { this.plugin = plugin; this.connectionSide = connectionSide; this.packetsID = Sets.newHashSet(packets); @@ -50,11 +50,28 @@ public abstract class PacketAdapter implements PacketListener { return packetsID; } + /** + * Retrieves the plugin associated with this listener. + * @return The associated plugin. + */ + public Plugin getPlugin() { + return plugin; + } + @Override public String toString() { + String name = ""; + + // Try to get the plugin name + try { + name = plugin.getName(); + } catch (NoSuchMethodError e) { + name = plugin.toString(); + } + // This is used by the error reporter return String.format("PacketAdapter[plugin=%s, side=%s, packets=%s]", - plugin.getName(), getConnectionSide().name(), - StringUtils.join(packetsID, ", ")); + name, getConnectionSide().name(), + Joiner.on(", ").join(packetsID)); } } diff --git a/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java index 5971e974..49bcea17 100644 --- a/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java @@ -1,6 +1,5 @@ package com.comphenix.protocol.events; -import org.apache.commons.lang.NullArgumentException; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.inventory.ItemStack; @@ -48,7 +47,7 @@ public class PacketContainer { */ public PacketContainer(int id, Packet handle, StructureModifier structure) { if (handle == null) - throw new NullArgumentException("handle"); + throw new IllegalArgumentException("handle cannot be null."); this.id = id; this.handle = handle; diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/MinecraftRegistry.java b/ProtocolLib/src/com/comphenix/protocol/injector/MinecraftRegistry.java index 9c8d4fe6..0620d67f 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/MinecraftRegistry.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/MinecraftRegistry.java @@ -4,12 +4,11 @@ import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -import org.apache.commons.lang.ObjectUtils; - import net.minecraft.server.Packet; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.google.common.base.Objects; /** * Static registries in Minecraft. @@ -78,7 +77,7 @@ class MinecraftRegistry { // Will most likely not be used for (Map.Entry entry : getPacketToID().entrySet()) { - if (ObjectUtils.equals(entry.getValue(), packetID)) { + if (Objects.equal(entry.getValue(), packetID)) { return entry.getKey(); } } diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java index 804d0391..c2ea14db 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java @@ -2,6 +2,7 @@ package com.comphenix.protocol.injector; import java.io.DataInputStream; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -11,8 +12,10 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.minecraft.server.Packet; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; -import org.apache.commons.lang.NullArgumentException; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -24,9 +27,11 @@ import org.bukkit.plugin.PluginManager; import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.events.ConnectionSide; +import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketListener; +import com.comphenix.protocol.reflect.FuzzyReflection; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -47,6 +52,9 @@ public final class PacketFilterManager implements ProtocolManager { // Whether or not this class has been closed private boolean hasClosed; + // The default class loader + private ClassLoader classLoader; + // Error logger private Logger logger; @@ -55,12 +63,13 @@ public final class PacketFilterManager implements ProtocolManager { */ public PacketFilterManager(ClassLoader classLoader, Logger logger) { if (logger == null) - throw new NullArgumentException("logger"); + throw new IllegalArgumentException("logger cannot be NULL."); if (classLoader == null) - throw new NullArgumentException("classLoader"); + throw new IllegalArgumentException("classLoader cannot be NULL."); try { // Initialize values + this.classLoader = classLoader; this.logger = logger; this.packetInjector = new PacketInjector(classLoader, this, connectionLookup); } catch (IllegalAccessException e) { @@ -80,7 +89,7 @@ public final class PacketFilterManager implements ProtocolManager { @Override public void addPacketListener(PacketListener listener) { if (listener == null) - throw new NullArgumentException("listener"); + throw new IllegalArgumentException("listener cannot be NULL."); packetListeners.add(listener); enablePacketFilters(listener.getConnectionSide(), @@ -90,13 +99,29 @@ public final class PacketFilterManager implements ProtocolManager { @Override public void removePacketListener(PacketListener listener) { if (listener == null) - throw new NullArgumentException("listener"); + throw new IllegalArgumentException("listener cannot be NULL"); packetListeners.remove(listener); disablePacketFilters(listener.getConnectionSide(), listener.getPacketsID()); } + @Override + public void removePacketAdapters(Plugin plugin) { + + // Iterate through every packet listener + for (Object listener : packetListeners.toArray()) { + if (listener instanceof PacketAdapter) { + PacketAdapter adapter = (PacketAdapter) listener; + + // Remove the listener + if (adapter.getPlugin().equals(plugin)) { + packetListeners.remove(listener); + } + } + } + } + /** * Invokes the given packet event for every registered listener. * @param event - the packet event to invoke. @@ -150,7 +175,7 @@ public final class PacketFilterManager implements ProtocolManager { */ private void enablePacketFilters(ConnectionSide side, Set packets) { if (side == null) - throw new NullArgumentException("side"); + throw new IllegalArgumentException("side cannot be NULL."); for (int packetID : packets) { if (side.isForServer()) @@ -167,7 +192,7 @@ public final class PacketFilterManager implements ProtocolManager { */ private void disablePacketFilters(ConnectionSide side, Set packets) { if (side == null) - throw new NullArgumentException("side"); + throw new IllegalArgumentException("side cannot be NULL."); for (int packetID : packets) { if (side.isForServer()) @@ -185,9 +210,9 @@ public final class PacketFilterManager implements ProtocolManager { @Override public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException { if (reciever == null) - throw new NullArgumentException("reciever"); + throw new IllegalArgumentException("reciever cannot be NULL."); if (packet == null) - throw new NullArgumentException("packet"); + throw new IllegalArgumentException("packet cannot be NULL."); getInjector(reciever).sendServerPacket(packet.getHandle(), filters); } @@ -201,9 +226,9 @@ public final class PacketFilterManager implements ProtocolManager { public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException { if (sender == null) - throw new NullArgumentException("sender"); + throw new IllegalArgumentException("sender cannot be NULL."); if (packet == null) - throw new NullArgumentException("packet"); + throw new IllegalArgumentException("packet cannot be NULL."); PlayerInjector injector = getInjector(sender); Packet mcPacket = packet.getHandle(); @@ -277,18 +302,90 @@ public final class PacketFilterManager implements ProtocolManager { * @param plugin - the parent plugin. */ public void registerEvents(PluginManager manager, Plugin plugin) { - manager.registerEvents(new Listener() { + + try { + manager.registerEvents(new Listener() { + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent event) { + injectPlayer(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerQuit(PlayerQuitEvent event) { + uninjectPlayer(event.getPlayer()); + } + }, plugin); + + } catch (NoSuchMethodError e) { + // Oh wow! We're running on 1.0.0 or older. + registerOld(manager, plugin); + } + } + + // Yes, this is crazy. + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void registerOld(PluginManager manager, Plugin plugin) { + + try { + ClassLoader loader = manager.getClass().getClassLoader(); - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPlayerJoin(PlayerJoinEvent event) { - injectPlayer(event.getPlayer()); - } + // The different enums we are going to need + Class eventTypes = loader.loadClass("org.bukkit.event.Event$Type"); + Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority"); - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPlayerQuit(PlayerQuitEvent event) { - uninjectPlayer(event.getPlayer()); - } - }, plugin); + // Get the priority + Object priorityNormal = Enum.valueOf(eventPriority, "Normal"); + + // Get event types + Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN"); + Object playerQuitType = Enum.valueOf(eventTypes, "PLAYER_QUIT"); + + // The player listener! Good times. + Class playerListener = loader.loadClass("org.bukkit.event.player.PlayerListener"); + + // Find the register event method + Method registerEvent = FuzzyReflection.fromObject(manager).getMethodByParameters("registerEvent", + eventTypes, Listener.class, eventPriority, Plugin.class); + + Enhancer ex = new Enhancer(); + ex.setSuperclass(playerListener); + ex.setClassLoader(classLoader); + ex.setCallback(new MethodInterceptor() { + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + + // Must have a parameter + if (args.length == 1) { + Object event = args[0]; + + // Check for the correct event + if (event instanceof PlayerJoinEvent) + injectPlayer(((PlayerJoinEvent) event).getPlayer()); + else if (event instanceof PlayerQuitEvent) + injectPlayer(((PlayerQuitEvent) event).getPlayer()); + } + + return null; + } + }); + + // Create our listener + Object proxy = ex.create(); + + registerEvent.invoke(manager, playerJoinType, proxy, priorityNormal, plugin); + registerEvent.invoke(manager, playerQuitType, proxy, priorityNormal, plugin); + + // A lot can go wrong + } catch (ClassNotFoundException e1) { + e1.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } } private void uninjectPlayer(Player player) { diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PacketInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/PacketInjector.java index f143b0d8..cbb270e9 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PacketInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PacketInjector.java @@ -15,6 +15,7 @@ import net.sf.cglib.proxy.Enhancer; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; /** @@ -49,10 +50,10 @@ class PacketInjector { 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(Packet.class).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT); + Field intHashMapField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT); try { - intHashMap = intHashMapField.get(null); + intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true); } catch (IllegalArgumentException e) { throw new RuntimeException("Minecraft is incompatible.", e); } diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java b/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java index a3c59f95..cf8708a1 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/ReadPacketModifier.java @@ -2,10 +2,9 @@ package com.comphenix.protocol.injector; import java.io.DataInputStream; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.WeakHashMap; -import org.apache.commons.lang.ArrayUtils; - import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; @@ -55,7 +54,7 @@ class ReadPacketModifier implements MethodInterceptor { // Is this a readPacketData method? if (returnValue == null && - ArrayUtils.isEquals(method.getParameterTypes(), parameters)) { + Arrays.equals(method.getParameterTypes(), parameters)) { // We need this in order to get the correct player DataInputStream input = (DataInputStream) args[0]; diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/DefaultInstances.java b/ProtocolLib/src/com/comphenix/protocol/reflect/DefaultInstances.java index b0747261..4b7669e4 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/DefaultInstances.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/DefaultInstances.java @@ -6,11 +6,9 @@ import java.util.*; import javax.annotation.Nullable; -import org.apache.commons.lang.ArrayUtils; - import com.google.common.base.Defaults; import com.google.common.base.Function; -import com.google.gson.internal.Primitives; +import com.google.common.base.Objects; /** * Used to construct default instances of any type. @@ -91,7 +89,7 @@ public class DefaultInstances { // Note that we don't allow recursive types - that is, types that // require itself in the constructor. if (types.length < lastCount) { - if (!ArrayUtils.contains(types, type)) { + if (!contains(types, type)) { minimum = candidate; lastCount = types.length; @@ -124,6 +122,15 @@ public class DefaultInstances { return null; } + private static boolean contains(T[] elements, T elementToFind) { + // Search for the given element in the array + for (T element : elements) { + if (Objects.equal(elementToFind, element)) + return true; + } + return false; + } + /** * Provides constructors for primtive types, wrappers, arrays and strings. * @author Kristian @@ -133,10 +140,10 @@ public class DefaultInstances { @Override public Object apply(@Nullable Class type) { - if (Primitives.isPrimitive(type)) { + if (PrimitiveUtils.isPrimitive(type)) { return Defaults.defaultValue(type); - } else if (Primitives.isWrapperType(type)) { - return Defaults.defaultValue(Primitives.unwrap(type)); + } else if (PrimitiveUtils.isWrapperType(type)) { + return Defaults.defaultValue(PrimitiveUtils.unwrap(type)); } else if (type.isArray()) { Class arrayType = type.getComponentType(); return Array.newInstance(arrayType, 0); diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/FieldUtils.java b/ProtocolLib/src/com/comphenix/protocol/reflect/FieldUtils.java index 28b9ebf8..2d852fdd 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/FieldUtils.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/FieldUtils.java @@ -21,9 +21,9 @@ import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Iterator; - -import org.apache.commons.lang.ClassUtils; +import java.util.List; /** * Utilities for working with fields by reflection. Adapted and refactored from @@ -119,11 +119,11 @@ public class FieldUtils { } } // check the public interface case. This must be manually searched for - // incase there is a public supersuperclass field hidden by a + // in case there is a public supersuperclass field hidden by a // private/package // superclass field. Field match = null; - for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf.hasNext();) { + for (Iterator intf = getAllInterfaces(cls).iterator(); intf.hasNext();) { try { Field test = ((Class) intf.next()).getField(fieldName); if (match != null) { @@ -138,6 +138,44 @@ public class FieldUtils { } return match; } + + /** + *

Gets a List of all interfaces implemented by the given + * class and its superclasses.

+ * + *

The order is determined by looking through each interface in turn as + * declared in the source file and following its hierarchy up. Then each + * superclass is considered in the same way. Later duplicates are ignored, + * so the order is maintained.

+ * + * @param cls the class to look up, may be null + * @return the List of interfaces in order, + * null if null input + */ + private static List getAllInterfaces(Class cls) { + if (cls == null) { + return null; + } + List list = new ArrayList(); + + while (cls != null) { + Class[] interfaces = cls.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (list.contains(interfaces[i]) == false) { + list.add(interfaces[i]); + } + List superInterfaces = getAllInterfaces(interfaces[i]); + for (Iterator it = superInterfaces.iterator(); it.hasNext();) { + Class intface = (Class) it.next(); + if (list.contains(intface) == false) { + list.add(intface); + } + } + } + cls = cls.getSuperclass(); + } + return list; + } /** * Read an accessible static Field. diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java index 2f729329..80ab95e5 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -2,12 +2,11 @@ package com.comphenix.protocol.reflect; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Pattern; -import org.apache.commons.lang.ArrayUtils; - /** * Retrieves fields and methods by signature, not just name. * @@ -106,7 +105,7 @@ public class FuzzyReflection { // Find the correct method to call for (Method method : getMethods()) { - if (ArrayUtils.isEquals(method.getParameterTypes(), args)) { + if (Arrays.equals(method.getParameterTypes(), args)) { return method; } } diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/PrimitiveUtils.java b/ProtocolLib/src/com/comphenix/protocol/reflect/PrimitiveUtils.java new file mode 100644 index 00000000..7e7cdd6f --- /dev/null +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/PrimitiveUtils.java @@ -0,0 +1,124 @@ +package com.comphenix.protocol.reflect; + +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Contains static utility methods pertaining to primitive types and their + * corresponding wrapper types. + * + * @author Kevin Bourrillion + */ +public final class PrimitiveUtils { + private PrimitiveUtils() { + } + + /** A map from primitive types to their corresponding wrapper types. */ + private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; + + /** A map from wrapper types to their corresponding primitive types. */ + private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; + + // Sad that we can't use a BiMap. :( + static { + Map, Class> primToWrap = new HashMap, Class>(16); + Map, Class> wrapToPrim = new HashMap, Class>(16); + + add(primToWrap, wrapToPrim, boolean.class, Boolean.class); + add(primToWrap, wrapToPrim, byte.class, Byte.class); + add(primToWrap, wrapToPrim, char.class, Character.class); + add(primToWrap, wrapToPrim, double.class, Double.class); + add(primToWrap, wrapToPrim, float.class, Float.class); + add(primToWrap, wrapToPrim, int.class, Integer.class); + add(primToWrap, wrapToPrim, long.class, Long.class); + add(primToWrap, wrapToPrim, short.class, Short.class); + add(primToWrap, wrapToPrim, void.class, Void.class); + + PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); + WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); + } + + private static void add(Map, Class> forward, + Map, Class> backward, Class key, Class value) { + forward.put(key, value); + backward.put(value, key); + } + + /** + * Returns true if this type is a primitive. + */ + public static boolean isPrimitive(Type type) { + return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type); + } + + /** + * Returns {@code true} if {@code type} is one of the nine primitive-wrapper + * types, such as {@link Integer}. + * + * @see Class#isPrimitive + */ + public static boolean isWrapperType(Type type) { + return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(checkNotNull(type)); + } + + /** + * Returns the corresponding wrapper type of {@code type} if it is a + * primitive type; otherwise returns {@code type} itself. Idempotent. + * + *
+	 *     wrap(int.class) == Integer.class
+	 *     wrap(Integer.class) == Integer.class
+	 *     wrap(String.class) == String.class
+	 * 
+ */ + public static Class wrap(Class type) { + // cast is safe: long.class and Long.class are both of type Class + @SuppressWarnings("unchecked") + Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE + .get(checkNotNull(type)); + return (wrapped == null) ? type : wrapped; + } + + /** + * Returns the corresponding primitive type of {@code type} if it is a + * wrapper type; otherwise returns {@code type} itself. Idempotent. + * + *
+	 *     unwrap(Integer.class) == int.class
+	 *     unwrap(int.class) == int.class
+	 *     unwrap(String.class) == String.class
+	 * 
+ */ + public static Class unwrap(Class type) { + // cast is safe: long.class and Long.class are both of type Class + @SuppressWarnings("unchecked") + Class unwrapped = (Class) WRAPPER_TO_PRIMITIVE_TYPE + .get(checkNotNull(type)); + return (unwrapped == null) ? type : unwrapped; + } + + public static T checkNotNull(T obj) { + if (obj == null) { + throw new NullPointerException(); + } + return obj; + } +} diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/StructureModifier.java b/ProtocolLib/src/com/comphenix/protocol/reflect/StructureModifier.java index a5d7f2f8..e7d76219 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/StructureModifier.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/StructureModifier.java @@ -11,7 +11,6 @@ import java.util.Set; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; -import com.google.gson.internal.Primitives; import net.minecraft.server.Packet; @@ -287,7 +286,7 @@ public class StructureModifier { Class type = field.getType(); // First, ignore primitive fields - if (!Primitives.isPrimitive(type)) { + if (!PrimitiveUtils.isPrimitive(type)) { // Next, see if we actually can generate a default value if (DefaultInstances.getDefault(type) != null) { // If so, require it