package org.develnext.jphp.json.gson;
import com.google.gson.*;
import org.develnext.jphp.json.JsonSerializable;
import php.runtime.Memory;
import php.runtime.common.collections.map.HashedMap;
import php.runtime.env.Environment;
import php.runtime.lang.ForeachIterator;
import php.runtime.lang.IObject;
import php.runtime.memory.ArrayMemory;
import php.runtime.memory.ObjectMemory;
import php.runtime.memory.StringMemory;
import php.runtime.reflection.ClassEntity;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class MemorySerializer implements JsonSerializer<Memory> {
protected boolean forceObject;
protected boolean numericCheck;
protected WeakReference<Environment> env;
protected Map<Memory.Type, Handler> typeHandlers;
protected Map<String, Handler> classHandlers;
{
typeHandlers = new HashedMap<Memory.Type, Handler>(1);
classHandlers = new HashedMap<String, Handler>(1);
}
public Environment getEnv() {
return env == null ? null : env.get();
}
public void setEnv(Environment env) {
this.env = env == null ? null : new WeakReference<Environment>(env);
}
public boolean isForceObject() {
return forceObject;
}
public void setForceObject(boolean forceObject) {
this.forceObject = forceObject;
}
public boolean isNumericCheck() {
return numericCheck;
}
public void setNumericCheck(boolean numericCheck) {
this.numericCheck = numericCheck;
}
protected JsonElement convert(Memory src, Set<Integer> used, boolean useHandlers) {
if (useHandlers) {
Handler handler = typeHandlers.get(src.getRealType());
if (handler != null) {
return convert(handler.call(getEnv(), src), used, false);
}
}
switch (src.getRealType()) {
case BOOL:
return new JsonPrimitive(src.toBoolean());
case DOUBLE: return new JsonPrimitive(src.toDouble());
case INT: return new JsonPrimitive(src.toLong());
case STRING: {
if (numericCheck) {
Memory m = StringMemory.toLong(src.toString());
if (m != null)
return new JsonPrimitive(m.toLong());
else
return new JsonPrimitive(src.toString());
} else
return new JsonPrimitive(src.toString());
}
case NULL: return JsonNull.INSTANCE;
case ARRAY: {
if (used.add(src.getPointer())){
JsonArray array = new JsonArray();
JsonObject object = new JsonObject();
boolean isList = !forceObject && src.toValue(ArrayMemory.class).isList();
ForeachIterator iterator = src.toValue(ArrayMemory.class).foreachIterator(false, false);
while (iterator.next()) {
if (isList)
array.add(convert(iterator.getValue(), used, useHandlers));
else
object.add(iterator.getKey().toString(), convert(iterator.getValue(), used, useHandlers));
}
used.remove(src.getPointer());
return isList ? array : object;
} else
return JsonNull.INSTANCE;
}
case OBJECT: {
if (used.add(src.getPointer())) {
IObject object = src.toValue(ObjectMemory.class).value;
if (useHandlers) {
Handler handler;
ClassEntity pr = object.getReflection();
do {
handler = classHandlers.get(pr.getLowerName());
pr = pr.getParent();
if (pr == null)
break;
} while (handler == null);
if (handler != null) {
return convert(handler.call(getEnv(), src), used, false);
}
}
if (object instanceof JsonSerializable) {
Environment env = this.getEnv() == null ? Environment.current() : this.getEnv();
env.pushCall(object, "jsonSerialize");
try {
Memory r = ((JsonSerializable) object).jsonSerialize(Environment.current());
return convert(r, used, useHandlers);
} finally {
env.popCall();
}
} else {
ForeachIterator iterator = object.getProperties().foreachIterator(false, false);
JsonObject r = new JsonObject();
while (iterator.next()) {
String key = iterator.getKey().toString();
if (!key.startsWith("\0"))
r.add(iterator.getKey().toString(), convert(iterator.getValue(), used, useHandlers));
}
return r;
}
} else
return JsonNull.INSTANCE;
}
default:
return JsonNull.INSTANCE;
}
}
public JsonElement serialize(Memory src, Type typeOfSrc, JsonSerializationContext context) {
return convert(src, new HashSet<Integer>(), true);
}
public void setTypeHandler(Memory.Type type, Handler handler) {
if (handler == null) {
typeHandlers.remove(type);
} else
typeHandlers.put(type, handler);
}
public void setClassHandler(String className, Handler handler) {
className = className.toLowerCase();
if (handler == null)
classHandlers.remove(className);
else
classHandlers.put(className, handler);
}
public interface Handler {
Memory call(Environment env, Memory value);
}
}