package php.runtime.memory.support;
import php.runtime.Memory;
import php.runtime.env.CompileScope;
import php.runtime.env.Environment;
import php.runtime.env.TraceInfo;
import php.runtime.exceptions.CriticalException;
import php.runtime.lang.BaseWrapper;
import php.runtime.lang.IObject;
import php.runtime.memory.ObjectMemory;
import php.runtime.memory.StringMemory;
import php.runtime.memory.support.operation.*;
import php.runtime.memory.support.operation.array.*;
import php.runtime.memory.support.operation.collection.HashSetMemoryOperation;
import php.runtime.memory.support.operation.collection.ListMemoryOperation;
import php.runtime.memory.support.operation.collection.SetMemoryOperation;
import php.runtime.memory.support.operation.iterator.IterableMemoryOperation;
import php.runtime.memory.support.operation.map.HashMapMemoryOperation;
import php.runtime.memory.support.operation.map.MapMemoryOperation;
import php.runtime.memory.support.operation.map.PropertiesMemoryOperation;
import php.runtime.reflection.ParameterEntity;
import php.runtime.reflection.support.ReflectionUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
abstract public class MemoryOperation<T> {
protected final static Map<Class<?>, Class<? extends BaseWrapper>> wrappers = new HashMap<Class<?>, Class<? extends BaseWrapper>>();
protected final static Map<Class<? extends BaseWrapper>, Class<?>> wrappersOut = new HashMap<Class<? extends BaseWrapper>, Class<?>>();
protected final static Map<Class<?>, MemoryOperation> operations = new HashMap<Class<?>, MemoryOperation>();
protected final static Map<ParametrizedClass, MemoryOperation> genericOperations = new HashMap<ParametrizedClass, MemoryOperation>();
abstract public Class<?>[] getOperationClasses();
final public T convertNoThrow(Environment env, TraceInfo trace, Memory arg) {
try {
return convert(env, trace, arg);
} catch (Throwable throwable) {
env.forwardThrow(throwable);
return null;
}
}
final public Memory unconvertNoThow(Environment env, TraceInfo trace, T arg) {
try {
return unconvert(env, trace, arg);
} catch (Throwable throwable) {
env.forwardThrow(throwable);
return Memory.NULL;
}
}
abstract public T convert(Environment env, TraceInfo trace, Memory arg) throws Throwable;
abstract public Memory unconvert(Environment env, TraceInfo trace, T arg) throws Throwable;
public void releaseConverted(Environment env, TraceInfo info, T arg) {
// nop
}
public Type[] getGenericTypes() {
return null;
}
public void applyTypeHinting(ParameterEntity parameter) {
// nop
}
protected MemoryOperation<T> instance(Type... genericTypes) {
return this;
}
public static <T> Class<? extends BaseWrapper> getWrapper(Class<T> clazz) {
return wrappers.get(clazz);
}
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassOfWrapper(Class<? extends BaseWrapper<T>> clazz) {
return (Class<T>) wrappersOut.get(clazz);
}
@SuppressWarnings("unchecked")
public static MemoryOperation get(final Class<?> type, Type genericTypes) {
return get(type, genericTypes, false);
}
@SuppressWarnings("unchecked")
public static MemoryOperation get(final Class<?> type, Type genericTypes, boolean includeParents) {
MemoryOperation operation = null;
if (genericTypes instanceof ParameterizedType) {
operation = genericOperations.get(new ParametrizedClass(type, ((ParameterizedType) genericTypes).getActualTypeArguments()));
}
if (operation == null) {
operation = operations.get(type);
if (operation == null) {
if (type.isArray()) {
MemoryOperation arrayMemoryOperation = new ArrayMemoryOperation(type);
register(arrayMemoryOperation);
return arrayMemoryOperation;
}
if (Enum.class.isAssignableFrom(type)) {
return new MemoryOperation() {
@Override
public Class<?>[] getOperationClasses() {
return new Class<?>[]{Enum.class};
}
@Override
@SuppressWarnings("unchecked")
public Object convert(Environment env, TraceInfo trace, Memory arg) throws Throwable {
return arg.isNull() ? null : Enum.valueOf((Class<? extends Enum>) type, arg.toString());
}
@Override
public Memory unconvert(Environment env, TraceInfo trace, Object arg) throws Throwable {
return arg == null ? Memory.NULL : StringMemory.valueOf(((Enum) arg).name());
}
@Override
public void applyTypeHinting(ParameterEntity parameter) {
parameter.setTypeEnum((Class<? extends Enum>) type);
}
};
}
final Class<? extends BaseWrapper> wrapperClass = wrappers.get(type);
if (wrapperClass != null) {
Constructor<BaseWrapper> constructor;
try {
constructor = (Constructor<BaseWrapper>) wrapperClass.getConstructor(Environment.class, type);
} catch (NoSuchMethodException e) {
try {
constructor = (Constructor<BaseWrapper>) wrapperClass.getConstructor(Environment.class, Object.class);
} catch (NoSuchMethodException e1) {
throw new CriticalException(e);
}
}
final Constructor<BaseWrapper> finalConstructor = constructor;
return new MemoryOperation() {
@Override
public Class<?>[] getOperationClasses() {
return new Class<?>[0];
}
@Override
public Object convert(Environment env, TraceInfo trace, Memory arg) throws Throwable {
if (arg.isNull()) {
return null;
}
return arg.toObject(BaseWrapper.class).getWrappedObject();
}
@Override
public Memory unconvert(Environment env, TraceInfo trace, Object arg) throws Throwable {
if (arg == null) {
return Memory.NULL;
}
Constructor<BaseWrapper> constructorContext = finalConstructor;
Class<? extends BaseWrapper> wrapperClassContext = wrapperClass;
if (arg.getClass() != type) {
wrapperClassContext = wrappers.get(arg.getClass());
}
if (wrapperClassContext != null && wrapperClassContext != wrapperClass) {
constructorContext = (Constructor<BaseWrapper>) wrapperClassContext.getConstructor(Environment.class, arg.getClass());
}
try {
BaseWrapper instance = constructorContext.newInstance(env, arg);
return ObjectMemory.valueOf(instance.__getOriginInstance());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new CriticalException(e);
}
}
@Override
public void applyTypeHinting(ParameterEntity parameter) {
parameter.setTypeNativeClass(type);
}
};
} else if (IObject.class.isAssignableFrom(type)) {
return new MemoryOperation() {
@Override
public Class<?>[] getOperationClasses() {
return new Class<?>[]{IObject.class};
}
@Override
@SuppressWarnings("unchecked")
public Object convert(Environment env, TraceInfo trace, Memory arg) throws Throwable {
if (arg.isNull()) {
return null;
}
return arg.toObject((Class<? extends IObject>) type);
}
@Override
public Memory unconvert(Environment env, TraceInfo trace, Object arg) throws Throwable {
if (arg == null) {
return Memory.NULL;
}
return ObjectMemory.valueOf((IObject) arg);
}
@Override
public void applyTypeHinting(ParameterEntity parameter) {
parameter.setType(ReflectionUtils.getClassName(type));
}
};
} else {
Class<?> superType = type.getSuperclass();
if (Object.class != superType && (includeParents || type.isAnonymousClass())) {
return get(superType, type.getGenericSuperclass(), includeParents);
}
}
}
}
if (operation == null) {
return null;
}
if (genericTypes instanceof ParameterizedType) {
return operation.instance(((ParameterizedType) genericTypes).getActualTypeArguments());
}
return operation;
}
@SuppressWarnings("unchecked")
public static void register(MemoryOperation operation) {
if (operation.getGenericTypes() != null) {
for (Class<?> type : operation.getOperationClasses()) {
genericOperations.put(new ParametrizedClass(type, operation.getGenericTypes()), operation);
}
} else {
for (Class<?> type : operation.getOperationClasses()) {
operations.put(type, operation);
}
}
}
public static <T> void registerWrapper(Class<T> clazz, Class<? extends BaseWrapper> wrapperClass) {
wrappers.put(clazz, wrapperClass);
wrappersOut.put(wrapperClass, clazz);
}
public static class ParametrizedClass<T> {
protected Class<T> clazz;
protected Type[] genericTypes;
public ParametrizedClass(Class<T> clazz, Type[] genericTypes) {
this.clazz = clazz;
this.genericTypes = genericTypes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ParametrizedClass)) return false;
ParametrizedClass that = (ParametrizedClass) o;
if (!clazz.equals(that.clazz)) return false;
if (!Arrays.equals(genericTypes, that.genericTypes)) return false;
return true;
}
@Override
public int hashCode() {
int result = clazz.hashCode();
result = 31 * result + Arrays.hashCode(genericTypes);
return result;
}
}
static {
register(new ObjectMemoryOperation());
register(new VoidMemoryOperation());
register(new MemoryMemoryOperation());
register(new ArrayMemoryMemoryOperation());
register(new BooleanMemoryOperation());
register(new LongMemoryOperation());
register(new IntegerMemoryOperation());
register(new ShortMemoryOperation());
register(new ByteMemoryOperation());
register(new DoubleMemoryOperation());
register(new FloatMemoryOperation());
register(new StringMemoryOperation());
register(new CharSequenceMemoryOperation());
register(new CharacterMemoryOperation());
register(new InvokerMemoryOperation());
register(new ForeachIteratorMemoryOperation());
register(new InputStreamMemoryOperation());
register(new OutputStreamMemoryOperation());
register(new FileMemoryOperation());
register(new ByteArrayInputStreamMemoryOperation());
register(new PatternMemoryOperation());
register(new IterableMemoryOperation());
register(new ListMemoryOperation());
register(new SetMemoryOperation());
register(new HashSetMemoryOperation());
register(new MapMemoryOperation());
register(new HashMapMemoryOperation());
register(new PropertiesMemoryOperation());
register(new UrlMemoryOperation());
register(new UriMemoryOperation());
register(new BinaryMemoryOperation());
register(new NumberMemoryOperation());
register(new BigDecimalOperation());
register(new BigIntegerOperation());
register(new ClassMemoryOperation());
register(new LocaleMemoryOperation());
register(new DateMemoryOperation());
register(new TimeZoneMemoryOperation());
register(new ScannerMemoryOperation());
register(new ThreadMemoryOperation());
register(new ThreadGroupMemoryOperation());
register(new FloatArrayMemoryOperation());
register(new DoubleArrayMemoryOperation());
register(new LongArrayMemoryOperation());
register(new IntegerArrayMemoryOperation());
register(new ShortArrayMemoryOperation());
register(new BooleanArrayMemoryOperation());
register(new CharArrayMemoryOperation());
}
}