package php.runtime.reflection.support;
import php.runtime.Memory;
import php.runtime.common.HintType;
import php.runtime.env.Environment;
import php.runtime.invoke.Invoker;
import php.runtime.lang.BaseWrapper;
import php.runtime.lang.IObject;
import php.runtime.memory.ObjectMemory;
import php.runtime.reflection.ClassEntity;
abstract public class TypeChecker {
abstract public String getSignature();
abstract public boolean check(Environment env, Memory value, boolean nullable);
public static TypeChecker of(HintType type) {
return new Simple(type);
}
public static TypeChecker of(String className) {
return new ClassName(className);
}
public static TypeChecker of(Class<?> typeNativeClass) {
return new NativeClass(typeNativeClass);
}
public static TypeChecker ofEnum(Class<? extends Enum> enumClass) {
return new EnumClass(enumClass);
}
public static class Simple extends TypeChecker {
protected HintType type;
public Simple(HintType type) {
this.type = type;
}
public HintType getType() {
return type;
}
@Override
public String getSignature() {
return type.toString();
}
@Override
public boolean check(Environment env, Memory value, boolean nullable) {
if (nullable && value.isNull())
return true;
switch (type){
case SCALAR:
switch (value.getRealType()){
case BOOL:
case INT:
case DOUBLE:
case STRING:
return true;
}
return false;
case OBJECT: return value.isObject();
case NUMBER: return value.isNumber();
case DOUBLE: return value.getRealType() == Memory.Type.DOUBLE;
case INT: return value.getRealType() == Memory.Type.INT;
case STRING: return value.isString();
case BOOLEAN: return value.getRealType() == Memory.Type.BOOL;
case ARRAY:
return value.isArray();
case TRAVERSABLE:
return value.isArray() || value.instanceOf("Traversable", "traversable");
case CALLABLE:
Invoker invoker = Invoker.valueOf(env, null, value);
return invoker != null && invoker.canAccess(env) == 0;
default:
return true;
}
}
}
public static class ClassName extends TypeChecker {
protected String typeClass;
protected String typeClassLower;
public ClassName(String typeClass) {
this.typeClass = typeClass;
this.typeClassLower = typeClass.toLowerCase();
}
public String getTypeClass() {
return typeClass;
}
public String getTypeClassLower() {
return typeClassLower;
}
@Override
public String getSignature() {
return typeClass;
}
@Override
public boolean check(Environment env, Memory value, boolean nullable) {
if (nullable && value.isNull())
return true;
if (!value.isObject())
return false;
ObjectMemory object = value.toValue(ObjectMemory.class);
ClassEntity oEntity = object.getReflection();
return oEntity.isInstanceOfLower(typeClassLower);
}
}
public static class NativeClass extends TypeChecker {
protected Class<?> typeNativeClass;
public NativeClass(Class<?> typeNativeClass) {
this.typeNativeClass = typeNativeClass;
}
@Override
public String getSignature() {
return ReflectionUtils.getClassName(typeNativeClass);
}
@Override
public boolean check(Environment env, Memory value, boolean nullable) {
if (nullable && value.isNull()) {
return true;
}
if (value.isObject()) {
IObject object = value.toObject(IObject.class);
if (object instanceof BaseWrapper) {
return typeNativeClass.isAssignableFrom(((BaseWrapper) object).getWrappedObject().getClass());
}
return false;
} else {
return false;
}
}
}
public static class EnumClass extends TypeChecker {
protected Class<? extends Enum> typeEnum;
public EnumClass(Class<? extends Enum> typeEnum) {
this.typeEnum = typeEnum;
}
public Class<? extends Enum> getTypeEnum() {
return typeEnum;
}
@Override
public String getSignature() {
return null;
}
@Override
public boolean check(Environment env, Memory value, boolean nullable) {
try {
if (nullable && value.isNull()) {
return true;
}
Enum.valueOf(typeEnum, value.toString());
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
}
}