package xapi.gwt.model.service;
import com.google.gwt.core.client.GWT;
import com.google.gwt.reflect.client.strategy.GwtRetention;
import xapi.annotation.compile.MagicMethod;
import xapi.annotation.inject.SingletonOverride;
import xapi.collect.X_Collect;
import xapi.collect.api.ClassTo;
import xapi.collect.api.StringDictionary;
import xapi.dev.source.CharBuffer;
import xapi.except.NotConfiguredCorrectly;
import xapi.io.X_IO;
import xapi.io.api.DelegatingIOCallback;
import xapi.log.X_Log;
import xapi.model.X_Model;
import xapi.model.api.Model;
import xapi.model.api.ModelKey;
import xapi.model.api.ModelQuery;
import xapi.model.api.ModelQueryResult;
import xapi.model.api.ModelSerializer;
import xapi.model.api.PrimitiveSerializer;
import xapi.model.impl.AbstractModelService;
import xapi.model.impl.ModelSerializerDefault;
import xapi.model.service.ModelService;
import xapi.platform.GwtPlatform;
import xapi.source.impl.StringCharIterator;
import xapi.util.api.ProvidesValue;
import xapi.util.api.SuccessHandler;
@GwtPlatform
@SuppressWarnings({"rawtypes"})
@SingletonOverride(implFor=ModelService.class)
public class ModelServiceGwt extends AbstractModelService
{
@SuppressWarnings("unchecked")
private static ClassTo<ProvidesValue<? extends Model>> PROVIDERS = X_Collect.newClassMap(
Class.class.cast(ProvidesValue.class)
);
public static String REGISTER_CREATOR_METHOD = "registerCreator";
private static Class<? extends Model> implClassRef;
public static <M extends Model> String registerCreator(final Class<M> cls, final String type, final Class<? extends M> implClass, final ProvidesValue<M> provider) {
PROVIDERS.put(cls, provider);
implClassRef = implClass;
final ModelService modelService = X_Model.getService();
if (modelService instanceof ModelServiceGwt) {
final ModelServiceGwt service = (ModelServiceGwt) modelService;
service.classToTypeName.put(cls, type);
service.classToTypeName.put(implClass, type);
service.typeNameToClass.put(type, cls);
} else {
return modelService.register(cls);
}
implClassRef = null;
return type;
}
@Override
@MagicMethod(doNotVisit = true)
@GwtRetention(privacy = 0) // this method is magic. Do not try to expose it via generated reflection...
public <T extends Model> T create(final Class<T> key) {
final ProvidesValue<? extends Model> provider = PROVIDERS.get(key);
if (provider == null) {
throw new NotConfiguredCorrectly("Could not find provider for "+key+"; did you forget to call X_Model.register()?");
}
if (!classToTypeName.containsKey(key)) {
final T model = (T) provider.get();
classToTypeName.put(key, model.getType());
return model;
}
return (T) provider.get();
}
/**
* @see xapi.model.impl.AbstractModelService#getDefaultSerializer(java.lang.Class)
*/
@Override
protected <M extends Model> ModelSerializer getDefaultSerializer(final String typeName) {
return new ModelSerializerDefault<Model>() {
@Override
protected boolean isModelType(final Class<?> propertyType) {
return PROVIDERS.containsKey(propertyType);
}
@Override
protected boolean isIterableType(Class<?> propertyType) {
return super.isIterableType(propertyType);
}
};
}
@Override
public String register(final Class<? extends Model> model) {
super.register(model);
final String type = classToTypeName.get(model);
if (implClassRef != null) {
if (type != null) {
classToTypeName.put(implClassRef, type);
}
}
return type;
}
@Override
protected <M extends Model> void doPersist(final String type, final M model, final SuccessHandler<M> callback) {
final String url = getUrlBase()+"model/persist";
final StringDictionary<String> headers = X_Collect.newDictionary();
headers.setValue("X-Model-Type", model.getType());
final CharBuffer serialized = serialize(type, model);
X_IO.getIOService().post(url, serialized.toString(), headers, new DelegatingIOCallback<>(
resp -> {
final M deserialized = deserialize(type, new StringCharIterator(resp.body()));
callback.onSuccess(deserialized);
}, DelegatingIOCallback.failHandler(callback)));
}
protected String getUrlBase() {
return GWT.getHostPageBaseURL();
}
@Override
public <M extends Model> void load(final Class<M> type, final ModelKey modelKey, final SuccessHandler<M> callback) {
final String url = getUrlBase()+"model/load";
final String typeName = getTypeName(type);
final StringDictionary<String> headers = X_Collect.newDictionary();
headers.setValue("X-Model-Type", typeName);
final PrimitiveSerializer primitives = primitiveSerializer();
String ns = modelKey.getNamespace().length() == 0 ? "" : modelKey.getNamespace();
// The namespace might be empty, so we use the serialized form that can transmit "" without
// constructing an invalid uri.
ns = primitives.serializeString(ns);
final String kind = modelKey.getKind();
final String id = primitives.serializeInt(modelKey.getKeyType()) + modelKey.getId();
final String serialized = "/" + ns + "/"+kind+"/"+id;
X_IO.getIOService().get(url+serialized, headers, new DelegatingIOCallback<>(resp -> {
X_Log.error("Got response! "+resp.body());
final M deserialized = deserialize(type, new StringCharIterator(resp.body()));
callback.onSuccess(deserialized);
}, DelegatingIOCallback.failHandler(callback)));
}
@Override
public <M extends Model> void query(final Class<M> modelClass, final ModelQuery<M> query,
final SuccessHandler<ModelQueryResult<M>> callback) {
final String url = getUrlBase()+"model/query";
final String typeName = getTypeName(modelClass);
final StringDictionary<String> headers = X_Collect.newDictionary();
headers.setValue("X-Model-Type", typeName);
final PrimitiveSerializer primitives = primitiveSerializer();
String ns = query.getNamespace().length() == 0 ? "" : query.getNamespace();
// The namespace might be empty, so we use the serialized form that can transmit "" without
// constructing an invalid uri.
ns = primitives.serializeString(ns);
final String kind = primitives.serializeString(typeName);
final String serialized = "/" + ns + "/"+kind+"/"+query.serialize(this, primitives);
X_IO.getIOService().get(url+serialized, headers, new DelegatingIOCallback<>(resp -> {
X_Log.error("Got response! "+resp.body());
final StringCharIterator chars = new StringCharIterator(resp.body());
final String cursor = primitives.deserializeString(chars);
int numResults = primitives.deserializeInt(chars);
final ModelQueryResult<M> result = new ModelQueryResult<>(modelClass);
result.setCursor(cursor);
while (numResults --> 0) {
final M deserialized = deserialize(typeName, chars);
result.addModel(deserialized);
}
callback.onSuccess(result);
}, DelegatingIOCallback.failHandler(callback)));
}
@Override
public void query(final ModelQuery<Model> query, final SuccessHandler<ModelQueryResult<Model>> callback) {
final String url = getUrlBase()+"model/query";
final StringDictionary<String> headers = X_Collect.newDictionary();
final PrimitiveSerializer primitives = primitiveSerializer();
String ns = query.getNamespace().length() == 0 ? "" : query.getNamespace();
// The namespace might be empty, so we use the serialized form that can transmit "" without
// constructing an invalid uri.
ns = primitives.serializeString(ns);
final String kind = primitives.serializeString("");
final String serialized = "/" + ns + "/" + kind + "/" + query.serialize(this, primitives);
X_IO.getIOService().get(url+serialized, headers, new DelegatingIOCallback<>(resp -> {
X_Log.error("Got response! "+resp.body());
final StringCharIterator chars = new StringCharIterator(resp.body());
final String cursor = primitives.deserializeString(chars);
int numResults = primitives.deserializeInt(chars);
final ModelQueryResult<Model> result = new ModelQueryResult<>(Model.class);
result.setCursor(cursor);
while (numResults --> 0) {
final String typeName = primitives.deserializeString(chars);
final Model deserialized = deserialize(typeName, chars);
result.addModel(deserialized);
}
callback.onSuccess(result);
}, DelegatingIOCallback.failHandler(callback)));
}
@Override
protected boolean isClientToServer() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public <M extends Model> Class<M> typeToClass(final String kind) {
return (Class<M>) typeNameToClass.get(kind);
}
}