reshuffling methods, adding docs
This commit is contained in:
parent
207d91cd6c
commit
021304a5d8
12 changed files with 84 additions and 60 deletions
|
|
@ -1,18 +1,12 @@
|
||||||
package com.github.shautvast.reflective.array;
|
package com.github.shautvast.reflective.array;
|
||||||
|
|
||||||
import com.github.shautvast.reflective.array.base.*;
|
import com.github.shautvast.reflective.array.base.*;
|
||||||
import com.github.shautvast.reflective.java.Java;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory class for dynamically working with arrays
|
* Public interface for dynamically working with arrays (create, set and get operations)
|
||||||
*/
|
*/
|
||||||
public class ArrayFactory {
|
public class ArrayFactory {
|
||||||
|
|
||||||
private static final Map<String, ArrayCreator> creatorCache = new ConcurrentHashMap<>();
|
|
||||||
private static final Map<Class<?>, Map<String, Object>> setterCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new array of the specified type and dimensions
|
* Creates a new array of the specified type and dimensions
|
||||||
|
|
@ -22,37 +16,40 @@ public class ArrayFactory {
|
||||||
* @return an object that you can cast to the expected array type
|
* @return an object that you can cast to the expected array type
|
||||||
*/
|
*/
|
||||||
public static Object newInstance(Class<?> elementType, int... dimensions) {
|
public static Object newInstance(Class<?> elementType, int... dimensions) {
|
||||||
return getCreatorInstance(elementType, dimensions).newInstance();
|
return ArrayHandlerFactory.getCreatorInstance(elementType, dimensions).newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an Object value on an array
|
* Sets an Object value on an array
|
||||||
|
*
|
||||||
* @param array the array on which the value is set
|
* @param array the array on which the value is set
|
||||||
* @param index the array index
|
* @param index the array index
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, Object value) {
|
public static void set(Object array, int index, Object value) {
|
||||||
getSetterInstance(ObjectArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(ObjectArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an int value on an array
|
* Sets an int value on an array
|
||||||
|
*
|
||||||
* @param array the array on which the value is set
|
* @param array the array on which the value is set
|
||||||
* @param index the array index
|
* @param index the array index
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, int value) {
|
public static void set(Object array, int index, int value) {
|
||||||
getSetterInstance(IntArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(IntArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a byte value on an array
|
* Sets a byte value on an array
|
||||||
|
*
|
||||||
* @param array the array on which the value is set
|
* @param array the array on which the value is set
|
||||||
* @param index the array index
|
* @param index the array index
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, byte value) {
|
public static void set(Object array, int index, byte value) {
|
||||||
getSetterInstance(ByteArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(ByteArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,7 +60,7 @@ public class ArrayFactory {
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, short value) {
|
public static void set(Object array, int index, short value) {
|
||||||
getSetterInstance(ShortArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(ShortArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,7 +71,7 @@ public class ArrayFactory {
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, long value) {
|
public static void set(Object array, int index, long value) {
|
||||||
getSetterInstance(LongArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(LongArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -85,7 +82,7 @@ public class ArrayFactory {
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, float value) {
|
public static void set(Object array, int index, float value) {
|
||||||
getSetterInstance(FloatArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(FloatArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -96,17 +93,20 @@ public class ArrayFactory {
|
||||||
* @param value the value to set
|
* @param value the value to set
|
||||||
*/
|
*/
|
||||||
public static void set(Object array, int index, double value) {
|
public static void set(Object array, int index, double value) {
|
||||||
getSetterInstance(DoubleArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(DoubleArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void set(Object array, int index, char value) {
|
public static void set(Object array, int index, char value) {
|
||||||
getSetterInstance(CharArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(CharArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void set(Object array, int index, boolean value) {
|
public static void set(Object array, int index, boolean value) {
|
||||||
getSetterInstance(BooleanArraySetter.class, typeChecked(array)).set(array, index, value);
|
ArrayHandlerFactory.getSetterInstance(BooleanArraySetter.class, typeChecked(array)).set(array, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only checks if object is an array, not the type of that array. TODO
|
||||||
|
*/
|
||||||
private static Class<?> typeChecked(Object array) {
|
private static Class<?> typeChecked(Object array) {
|
||||||
Class<?> arrayType = array.getClass();
|
Class<?> arrayType = array.getClass();
|
||||||
if (!arrayType.isArray()) {
|
if (!arrayType.isArray()) {
|
||||||
|
|
@ -114,34 +114,4 @@ public class ArrayFactory {
|
||||||
}
|
}
|
||||||
return arrayType;
|
return arrayType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T getSetterInstance(Class<T> setterBaseType, Class<?> arrayType) {
|
|
||||||
String arrayTypeName = Java.internalName(arrayType);
|
|
||||||
String syntheticClassName = getSyntheticClassName(arrayType, arrayTypeName);
|
|
||||||
|
|
||||||
return (T) setterCache.computeIfAbsent(setterBaseType, k -> new ConcurrentHashMap<>()).
|
|
||||||
computeIfAbsent(syntheticClassName,
|
|
||||||
k -> AsmArrayFactory.createSyntheticArraySetter(setterBaseType, arrayTypeName, syntheticClassName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getSyntheticClassName(Class<?> arrayType, String arrayTypeName) {
|
|
||||||
return "com/shautvast/reflective/array/ArraySetter_"
|
|
||||||
+ javaName(arrayTypeName) + Java.getNumDimensions(arrayType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ArrayCreator getCreatorInstance(Class<?> elementType, int... dimensions) {
|
|
||||||
String elementTypeName = Java.internalName(elementType);
|
|
||||||
String syntheticClassName = "com/shautvast/reflective/array/ArrayCreator_"
|
|
||||||
+ javaName(elementTypeName) + dimensions.length;
|
|
||||||
|
|
||||||
return creatorCache.computeIfAbsent(syntheticClassName,
|
|
||||||
k -> AsmArrayFactory.createSyntheticArrayCreator(elementTypeName, syntheticClassName, dimensions));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String javaName(String arrayTypeName) {
|
|
||||||
return arrayTypeName
|
|
||||||
.replaceAll("[/.\\[;]", "")
|
|
||||||
.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.github.shautvast.reflective.array;
|
package com.github.shautvast.reflective.array;
|
||||||
|
|
||||||
import com.github.shautvast.reflective.array.base.ArrayCreator;
|
import com.github.shautvast.reflective.array.base.ArrayCreator;
|
||||||
|
import com.github.shautvast.reflective.array.base.ArraySetter;
|
||||||
import com.github.shautvast.reflective.array.base.ObjectArraySetter;
|
import com.github.shautvast.reflective.array.base.ObjectArraySetter;
|
||||||
import com.github.shautvast.reflective.java.ASM;
|
import com.github.shautvast.reflective.java.ASM;
|
||||||
import com.github.shautvast.reflective.java.ByteClassLoader;
|
import com.github.shautvast.reflective.java.ByteClassLoader;
|
||||||
|
|
@ -11,10 +12,55 @@ import org.objectweb.asm.tree.*;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static org.objectweb.asm.Opcodes.*;
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
class AsmArrayFactory {
|
class ArrayHandlerFactory {
|
||||||
|
/* cache for the compiled creator classes */
|
||||||
|
private static final Map<String, ArrayCreator> creatorCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/* Cache for the compiled setter classes.
|
||||||
|
* The outer Map contains the ArraySetter type (some primitive or Object Setter)
|
||||||
|
* That maps to the concrete calculated ArraySetter instance name which maps to the instance itself.
|
||||||
|
* TODO see if this can be optimized
|
||||||
|
*/
|
||||||
|
private static final Map<Class<? extends ArraySetter>, Map<String, Object>> setterCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generic method for creating array setters (primitives and objects)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T extends ArraySetter> T getSetterInstance(Class<T> setterBaseType, Class<?> arrayType) {
|
||||||
|
String arrayTypeName = Java.internalName(arrayType);
|
||||||
|
String syntheticClassName = "com/shautvast/reflective/array/ArraySetter_"
|
||||||
|
+ javaName(arrayTypeName) + Java.getNumDimensions(arrayType);
|
||||||
|
|
||||||
|
return (T) setterCache.computeIfAbsent(setterBaseType, k -> new ConcurrentHashMap<>()).
|
||||||
|
computeIfAbsent(syntheticClassName,
|
||||||
|
k -> ArrayHandlerFactory.createSyntheticArraySetter(setterBaseType, arrayTypeName, syntheticClassName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates an instance of an ArrayCreator of the specified type */
|
||||||
|
static ArrayCreator getCreatorInstance(Class<?> elementType, int... dimensions) {
|
||||||
|
String elementTypeName = Java.internalName(elementType);
|
||||||
|
String syntheticClassName = "com/shautvast/reflective/array/ArrayCreator_"
|
||||||
|
+ javaName(elementTypeName) + dimensions.length;
|
||||||
|
|
||||||
|
return creatorCache.computeIfAbsent(syntheticClassName,
|
||||||
|
k -> ArrayHandlerFactory.createSyntheticArrayCreator(elementTypeName, syntheticClassName, dimensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strips all disallowed characters from a classname */
|
||||||
|
private static String javaName(String arrayTypeName) {
|
||||||
|
return arrayTypeName
|
||||||
|
.replaceAll("[/.\\[;]", "")
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Creates the ASM ClassNode for an ArrayCreator */
|
||||||
static ArrayCreator createSyntheticArrayCreator(String elementType, String name, int... dimensions) {
|
static ArrayCreator createSyntheticArrayCreator(String elementType, String name, int... dimensions) {
|
||||||
ClassNode classNode = ASM.createDefaultClassNode(name, Java.internalName(ArrayCreator.class));
|
ClassNode classNode = ASM.createDefaultClassNode(name, Java.internalName(ArrayCreator.class));
|
||||||
classNode.methods.add(createNewInstanceMethodNode(elementType, dimensions));
|
classNode.methods.add(createNewInstanceMethodNode(elementType, dimensions));
|
||||||
|
|
@ -38,6 +84,7 @@ class AsmArrayFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Creates the ASM ClassNode for an ArraySetter */
|
||||||
static <T> T createSyntheticArraySetter(Class<T> setterType, String arrayType, String name) {
|
static <T> T createSyntheticArraySetter(Class<T> setterType, String arrayType, String name) {
|
||||||
ClassNode classNode = ASM.createDefaultClassNode(name, Java.internalName(setterType));
|
ClassNode classNode = ASM.createDefaultClassNode(name, Java.internalName(setterType));
|
||||||
classNode.methods.add(createSetMethodNode(setterType, arrayType));
|
classNode.methods.add(createSetMethodNode(setterType, arrayType));
|
||||||
|
|
@ -61,6 +108,7 @@ class AsmArrayFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Creates the newInstance method for ArrayCreator classes */
|
||||||
private static MethodNode createNewInstanceMethodNode(String componentType, int[] dimensions) {
|
private static MethodNode createNewInstanceMethodNode(String componentType, int[] dimensions) {
|
||||||
MethodNode methodNode = new MethodNode(ACC_PUBLIC,
|
MethodNode methodNode = new MethodNode(ACC_PUBLIC,
|
||||||
"newInstance", "()Ljava/lang/Object;", null, null);
|
"newInstance", "()Ljava/lang/Object;", null, null);
|
||||||
|
|
@ -71,6 +119,7 @@ class AsmArrayFactory {
|
||||||
return methodNode;
|
return methodNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Creates the set method for ArraySetter classes */
|
||||||
private static MethodNode createSetMethodNode(Class<?> setterType, String arrayType) {
|
private static MethodNode createSetMethodNode(Class<?> setterType, String arrayType) {
|
||||||
String elementType;
|
String elementType;
|
||||||
if (setterType == ObjectArraySetter.class) {
|
if (setterType == ObjectArraySetter.class) {
|
||||||
|
|
@ -99,7 +148,7 @@ class AsmArrayFactory {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "B":
|
case "B":
|
||||||
case "Z":
|
case "Z":
|
||||||
return new int[]{ILOAD, BASTORE};
|
return new int[]{ILOAD, BASTORE}; // load int, store byte??
|
||||||
case "S":
|
case "S":
|
||||||
return new int[]{ILOAD, SASTORE};
|
return new int[]{ILOAD, SASTORE};
|
||||||
case "I":
|
case "I":
|
||||||
|
|
@ -112,7 +161,6 @@ class AsmArrayFactory {
|
||||||
return new int[]{DLOAD, DASTORE};
|
return new int[]{DLOAD, DASTORE};
|
||||||
case "C":
|
case "C":
|
||||||
return new int[]{ILOAD, CASTORE};
|
return new int[]{ILOAD, CASTORE};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new int[]{ALOAD, AASTORE};
|
return new int[]{ALOAD, AASTORE};
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty interface because you can't be generic over primitives in Java
|
||||||
|
*/
|
||||||
|
public interface ArraySetter {
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class BooleanArraySetter {
|
public abstract class BooleanArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, boolean value);
|
public abstract void set(Object array, int index, boolean value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class ByteArraySetter {
|
public abstract class ByteArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, byte value);
|
public abstract void set(Object array, int index, byte value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class CharArraySetter {
|
public abstract class CharArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, char value);
|
public abstract void set(Object array, int index, char value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class DoubleArraySetter {
|
public abstract class DoubleArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, double value);
|
public abstract void set(Object array, int index, double value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class FloatArraySetter {
|
public abstract class FloatArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, float value);
|
public abstract void set(Object array, int index, float value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class IntArraySetter {
|
public abstract class IntArraySetter implements ArraySetter {
|
||||||
public abstract void set(Object array, int index, int value);
|
public abstract void set(Object array, int index, int value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class LongArraySetter {
|
public abstract class LongArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, long value);
|
public abstract void set(Object array, int index, long value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class ObjectArraySetter {
|
public abstract class ObjectArraySetter implements ArraySetter {
|
||||||
public abstract void set(Object array, int index, Object value);
|
public abstract void set(Object array, int index, Object value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.shautvast.reflective.array.base;
|
package com.github.shautvast.reflective.array.base;
|
||||||
|
|
||||||
public abstract class ShortArraySetter {
|
public abstract class ShortArraySetter implements ArraySetter {
|
||||||
|
|
||||||
public abstract void set(Object array, int index, short value);
|
public abstract void set(Object array, int index, short value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue