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;
|
||||
|
||||
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 {
|
||||
|
||||
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
|
||||
|
|
@ -22,37 +16,40 @@ public class ArrayFactory {
|
|||
* @return an object that you can cast to the expected array type
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param array the array on which the value is set
|
||||
* @param index the array index
|
||||
* @param value the value to set
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param array the array on which the value is set
|
||||
* @param index the array index
|
||||
* @param value the value to set
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param array the array on which the value is set
|
||||
* @param index the array index
|
||||
* @param value the value to set
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
Class<?> arrayType = array.getClass();
|
||||
if (!arrayType.isArray()) {
|
||||
|
|
@ -114,34 +114,4 @@ public class ArrayFactory {
|
|||
}
|
||||
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;
|
||||
|
||||
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.java.ASM;
|
||||
import com.github.shautvast.reflective.java.ByteClassLoader;
|
||||
|
|
@ -11,10 +12,55 @@ import org.objectweb.asm.tree.*;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
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) {
|
||||
ClassNode classNode = ASM.createDefaultClassNode(name, Java.internalName(ArrayCreator.class));
|
||||
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) {
|
||||
ClassNode classNode = ASM.createDefaultClassNode(name, Java.internalName(setterType));
|
||||
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) {
|
||||
MethodNode methodNode = new MethodNode(ACC_PUBLIC,
|
||||
"newInstance", "()Ljava/lang/Object;", null, null);
|
||||
|
|
@ -71,6 +119,7 @@ class AsmArrayFactory {
|
|||
return methodNode;
|
||||
}
|
||||
|
||||
/* Creates the set method for ArraySetter classes */
|
||||
private static MethodNode createSetMethodNode(Class<?> setterType, String arrayType) {
|
||||
String elementType;
|
||||
if (setterType == ObjectArraySetter.class) {
|
||||
|
|
@ -99,7 +148,7 @@ class AsmArrayFactory {
|
|||
switch (type) {
|
||||
case "B":
|
||||
case "Z":
|
||||
return new int[]{ILOAD, BASTORE};
|
||||
return new int[]{ILOAD, BASTORE}; // load int, store byte??
|
||||
case "S":
|
||||
return new int[]{ILOAD, SASTORE};
|
||||
case "I":
|
||||
|
|
@ -112,7 +161,6 @@ class AsmArrayFactory {
|
|||
return new int[]{DLOAD, DASTORE};
|
||||
case "C":
|
||||
return new int[]{ILOAD, CASTORE};
|
||||
|
||||
default:
|
||||
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;
|
||||
|
||||
public abstract class BooleanArraySetter {
|
||||
|
||||
public abstract class BooleanArraySetter implements ArraySetter {
|
||||
public abstract void set(Object array, int index, boolean value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue