183 lines
7.1 KiB
Java
183 lines
7.1 KiB
Java
package com.comphenix.protocol.wrappers;
|
|
|
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
|
import com.comphenix.protocol.utility.ClassSource;
|
|
import com.google.common.base.Function;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Wrap a GNU Trove Collection class with an equivalent Java Collection class.
|
|
* @author Kristian
|
|
*/
|
|
public class TroveWrapper {
|
|
private static final String[] TROVE_LOCATIONS = new String[] {
|
|
"net.minecraft.util.gnu.trove", // For Minecraft 1.7.2
|
|
"gnu.trove" // For an old version of Spigot
|
|
};
|
|
|
|
// The different Trove versions
|
|
private static final ClassSource[] TROVE_SOURCES = {
|
|
ClassSource.fromPackage(TROVE_LOCATIONS[0]),
|
|
ClassSource.fromPackage(TROVE_LOCATIONS[1])
|
|
};
|
|
|
|
/**
|
|
* Retrieve a read-only field accessor that automatically wraps the underlying Trove instance.
|
|
* @param accessor - the accessor.
|
|
* @return The read only accessor.
|
|
*/
|
|
public static FieldAccessor wrapMapField(final FieldAccessor accessor) {
|
|
return wrapMapField(accessor, null);
|
|
}
|
|
|
|
/**
|
|
* Retrieve a read-only field accessor that automatically wraps the underlying Trove instance.
|
|
* @param accessor - the accessor.
|
|
* @param noEntryTransform - transform the no entry value, or NULL to ignore.
|
|
* @return The read only accessor.
|
|
*/
|
|
public static FieldAccessor wrapMapField(final FieldAccessor accessor, final Function<Integer, Integer> noEntryTransform) {
|
|
/*return new ReadOnlyFieldAccessor() {
|
|
@Override
|
|
public Object get(Object instance) {
|
|
Object troveMap = accessor.get(instance);
|
|
|
|
// Apply transform as well
|
|
if (noEntryTransform != null)
|
|
TroveWrapper.transformNoEntryValue(troveMap, noEntryTransform);
|
|
return getDecoratedMap(troveMap);
|
|
}
|
|
@Override
|
|
public Field getField() {
|
|
return accessor.getField();
|
|
}
|
|
};*/
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieve a Java wrapper for the corresponding Trove map.
|
|
* @param <TKey> Key type
|
|
* @param <TValue> Value type
|
|
* @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 <TValue> Type
|
|
* @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 <TValue> Type
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* Determine if the given class is found within gnu.trove.
|
|
* @param clazz - the clazz.
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
*/
|
|
public static boolean isTroveClass(Class<?> clazz) {
|
|
return getClassSource(clazz) != null;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the correct class source from the given class.
|
|
* @param clazz - the class source.
|
|
* @return The class source, or NULL if not found.
|
|
*/
|
|
private static ClassSource getClassSource(Class<?> clazz) {
|
|
for (int i = 0; i < TROVE_LOCATIONS.length; i++) {
|
|
if (clazz.getCanonicalName().startsWith(TROVE_LOCATIONS[i])) {
|
|
return TROVE_SOURCES[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the corresponding decorator.
|
|
* @param trove - the trove class.
|
|
* @return The wrapped trove class.
|
|
*/
|
|
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());
|
|
Class<?> decorators = getClassSource(trove.getClass()).loadClass("TDecorator")
|
|
.orElseThrow(() -> new IllegalStateException("TDecorators class not found"));
|
|
|
|
// 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() + ")");
|
|
}
|
|
|
|
public static class CannotFindTroveNoEntryValue extends RuntimeException {
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private CannotFindTroveNoEntryValue(Throwable inner) {
|
|
super("Cannot correct trove map.", inner);
|
|
}
|
|
}
|
|
}
|