ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedIntHashMap.java
2023-05-12 10:35:34 -04:00

182 lines
6.2 KiB
Java

package com.comphenix.protocol.wrappers;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import javax.annotation.Nonnull;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Preconditions;
/**
* Represents a wrapper for the internal IntHashMap in Minecraft.
* @author Kristian
* @deprecated IntHashMap no longer exists
*/
@Deprecated
public class WrappedIntHashMap extends AbstractWrapper {
private static Method PUT_METHOD;
private static Method GET_METHOD;
private static Method REMOVE_METHOD;
/**
* Construct an IntHashMap wrapper around an instance.
* @param handle - the NMS instance.
*/
private WrappedIntHashMap(Object handle) {
super(MinecraftReflection.getIntHashMapClass());
setHandle(handle);
}
/**
* Construct a new IntHashMap.
* @return A new IntHashMap.
*/
public static WrappedIntHashMap newMap() {
try {
return new WrappedIntHashMap(MinecraftReflection.getIntHashMapClass().newInstance());
} catch (Exception e) {
throw new RuntimeException("Unable to construct IntHashMap.", e);
}
}
/**
* Construct a wrapper around a given NMS IntHashMap.
* @param handle - the NMS IntHashMap.
* @return The created wrapped.
* @throws IllegalArgumentException If the handle is not an IntHasMap.
*/
public static WrappedIntHashMap fromHandle(@Nonnull Object handle) {
return new WrappedIntHashMap(handle);
}
/**
* Associates a specified key with the given value in the integer map.
* <p>
* If the key has already been associated with a value, then it will be replaced by the new value.
* @param key - the key to insert.
* @param value - the value to insert. Cannot be NULL.
* @throws RuntimeException If the reflection machinery failed.
*/
public void put(int key, Object value) {
Preconditions.checkNotNull(value, "value cannot be NULL.");
initializePutMethod();
putInternal(key, value);
}
/**
* Invoked when a value must be inserted into the underlying map, regardless of preconditions.
* @param key - the key.
* @param value - the value to insert.
*/
private void putInternal(int key, Object value) {
invokeMethod(PUT_METHOD, key, value);
}
/**
* Retrieve the value associated with a specific key, or NULL if not found.
* @param key - the integer key.
* @return The associated value, or NULL.
*/
public Object get(int key) {
initializeGetMethod();
return invokeMethod(GET_METHOD, key);
}
/**
* Remove a mapping of a key to a value if it is present.
* @param key - the key of the mapping to remove.
* @return The object that was removed, or NULL if the key is not present.
*/
public Object remove(int key) {
initializeGetMethod();
if (REMOVE_METHOD == null)
return removeFallback(key);
return invokeMethod(REMOVE_METHOD, key);
}
/**
* Remove a entry in the IntHashMap using a fallback method.
* @param key - the key of the mapping to remove.
* @return The removed element.
*/
private Object removeFallback(int key) {
Object old = get(key);
invokeMethod(PUT_METHOD, key, null);
return old;
}
/**
* Invoke a particular method on the current handle
* @param method - the current method.
* @param params - parameters.
* @return The return value, if any.
*/
private Object invokeMethod(Method method, Object... params) {
try {
return method.invoke(handle, params);
} 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("Unable to invoke " + method + " on " + handle, e);
}
}
private void initializePutMethod() {
if (PUT_METHOD == null) {
// Fairly straight forward
PUT_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getIntHashMapClass()).getMethod(
FuzzyMethodContract.newBuilder().
banModifier(Modifier.STATIC).
parameterCount(2).
parameterExactType(int.class).
parameterExactType(Object.class).
build());
}
}
private void initializeGetMethod() {
if (GET_METHOD == null) {
WrappedIntHashMap temp = WrappedIntHashMap.newMap();
String expected = "hello";
// Determine which method to trust
for (Method method : FuzzyReflection.fromClass(MinecraftReflection.getIntHashMapClass()).
getMethodListByParameters(Object.class, new Class<?>[] { int.class })) {
// Initialize a value
temp.put(1, expected);
// Skip static methods
if (Modifier.isStatic(method.getModifiers()))
continue;
try {
boolean first = expected.equals(method.invoke(temp.getHandle(), 1));
boolean second = expected.equals(method.invoke(temp.getHandle(), 1));
// See if we found the method we are looking for
if (first && !second) {
REMOVE_METHOD = method;
} else if (first && second) {
GET_METHOD = method;
}
} catch (Exception e) {
// Suppress
}
}
if (GET_METHOD == null)
throw new IllegalStateException("Unable to find appropriate GET_METHOD for IntHashMap.");
}
}
}