203 lines
6.0 KiB
Haxe
203 lines
6.0 KiB
Haxe
package ru.m.data;
|
|
|
|
import flash.net.SharedObject;
|
|
import haxe.DynamicAccess;
|
|
import haxe.io.Bytes;
|
|
import haxe.Serializer;
|
|
import haxe.Unserializer;
|
|
import hw.connect.PacketUtil;
|
|
import promhx.Promise;
|
|
import ru.m.data.IDataSource;
|
|
|
|
class Converter<D, S> {
|
|
private var serializer:D -> S;
|
|
private var desirealizer:S -> D;
|
|
|
|
public function new(serializer:D -> S, desirealizer:S -> D) {
|
|
this.serializer = serializer;
|
|
this.desirealizer = desirealizer;
|
|
}
|
|
|
|
public inline function serialize(item:D):S {
|
|
return serializer(item);
|
|
}
|
|
|
|
public inline function deserialize(data:S):D {
|
|
return desirealizer(data);
|
|
}
|
|
}
|
|
|
|
class DefaultConverter<D> extends Converter<D, String> {
|
|
public function new () {
|
|
super(
|
|
item -> Serializer.run(item),
|
|
data -> new Unserializer(data).unserialize()
|
|
);
|
|
}
|
|
}
|
|
|
|
class ProtoConverter<D:protohx.Message> extends Converter<D, String> {
|
|
public function new(messageClass:Class<D>) {
|
|
super(
|
|
item -> PacketUtil.toBytes(item).toHex(),
|
|
data -> PacketUtil.fromBytes(Bytes.ofHex(data), messageClass)
|
|
);
|
|
}
|
|
}
|
|
|
|
class EmptyConverter<T> extends Converter<T, T> {
|
|
public function new() {
|
|
super(item -> item, data -> data);
|
|
}
|
|
}
|
|
|
|
class MetaBuilder<D> extends DefaultConverter<DataMeta> {
|
|
private var builder:D -> DataMeta;
|
|
|
|
public function new(builder:D -> DataMeta) {
|
|
super();
|
|
this.builder = builder;
|
|
}
|
|
|
|
public function build(item:D):DataMeta {
|
|
return builder(item);
|
|
}
|
|
}
|
|
|
|
typedef DataMeta = Filter;
|
|
|
|
typedef IdValue<I> = {id:I, meta:DataMeta};
|
|
|
|
class DataStorage<I, D> implements IDataManager<I, D> implements IDataIndex<I> {
|
|
|
|
inline private static var DATA_KEY = "item";
|
|
|
|
private var name:String;
|
|
private var metaBuilder:MetaBuilder<D>;
|
|
private var idConverter:Converter<I, String>;
|
|
private var dataConverter:Converter<D, Dynamic>;
|
|
private var indexData:SharedObject;
|
|
|
|
public function new(
|
|
name:String,
|
|
metaBuilder:MetaBuilder<D>,
|
|
idConverter:Converter<I, String> = null,
|
|
dataConverter:Converter<D, Dynamic> = null
|
|
) {
|
|
this.name = name;
|
|
this.metaBuilder = metaBuilder;
|
|
this.idConverter = idConverter != null ? idConverter : new DefaultConverter();
|
|
this.dataConverter = dataConverter != null ? dataConverter : new DefaultConverter();
|
|
this.indexData = SharedObject.getLocal('${name}/index');
|
|
}
|
|
|
|
private function serializeId(id:I):String {
|
|
return idConverter.serialize(id);
|
|
}
|
|
|
|
private function desirealizeId(data:String):I {
|
|
return idConverter.deserialize(data);
|
|
}
|
|
|
|
private function buildMeta(item:D):DataMeta {
|
|
return metaBuilder.build(item);
|
|
}
|
|
|
|
private function checkFilter(filter:Null<Filter>, meta:DataMeta):Bool {
|
|
if (filter != null) {
|
|
for (k => v in filter) {
|
|
if (meta.exists(k) && meta.get(k) != v) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private function sort(order:Order, values:Array<IdValue<I>>):Void {
|
|
if (order != null) {
|
|
values.sort((a:IdValue<I>, b:IdValue<I>) -> {
|
|
for (item in order) {
|
|
var av = a.meta.get(item.key);
|
|
var bv = b.meta.get(item.key);
|
|
if (av > bv) {
|
|
return item.reverse ? - 1 : 1;
|
|
} else if (av < bv) {
|
|
return item.reverse ? 1 : -1;
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
private function serialize(item:D):Dynamic {
|
|
return dataConverter.serialize(item);
|
|
}
|
|
|
|
private function unserialize(data:Dynamic):D {
|
|
return dataConverter.deserialize(data);
|
|
}
|
|
|
|
public function getIndexPage(page:Page):Promise<DataPage<I>> {
|
|
var data:DynamicAccess<String> = indexData.data;
|
|
var values:Array<IdValue<I>> = [];
|
|
for (k => v in data) {
|
|
var meta = metaBuilder.deserialize(v);
|
|
if (checkFilter(page.filter, meta)) {
|
|
values.push({id: desirealizeId(k), meta: meta});
|
|
}
|
|
}
|
|
sort(page.order, values);
|
|
var result:Array<I> = values.slice(page.index * page.count, page.index * page.count + page.count).map(value -> value.id);
|
|
return Promise.promise({
|
|
page: page,
|
|
total: values.length,
|
|
data: result,
|
|
});
|
|
}
|
|
|
|
public function getPage(page:Page):Promise<DataPage<D>> {
|
|
return getIndexPage(page).pipe((indexPage:DataPage<I>) -> {
|
|
return Promise.whenAll([for (id in indexPage.data) get(id)]).then((data:Array<D>) -> {
|
|
return {
|
|
page: indexPage.page,
|
|
total: indexPage.total,
|
|
data: data,
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
public function get(id:I):Promise<D> {
|
|
var stringId = serializeId(id);
|
|
var itemData = SharedObject.getLocal('${name}/${stringId}');
|
|
var result:D = null;
|
|
if (Reflect.hasField(itemData.data, DATA_KEY)) {
|
|
result = unserialize(Reflect.field(itemData.data, DATA_KEY));
|
|
}
|
|
return Promise.promise(result);
|
|
}
|
|
|
|
public function save(item:D):Promise<D> {
|
|
var meta = metaBuilder.build(item);
|
|
var stringId = meta.get("id");
|
|
var itemData = SharedObject.getLocal('${name}/${stringId}');
|
|
itemData.setProperty(DATA_KEY, serialize(item));
|
|
itemData.flush();
|
|
indexData.setProperty(stringId, metaBuilder.serialize(meta));
|
|
indexData.flush();
|
|
return Promise.promise(item);
|
|
}
|
|
|
|
public function delete(id:I):Promise<Bool> {
|
|
var stringId = serializeId(id);
|
|
var itemData = SharedObject.getLocal('${name}/${stringId}');
|
|
itemData.clear();
|
|
var data:DynamicAccess<String> = indexData.data;
|
|
data.remove(stringId);
|
|
indexData.flush();
|
|
return Promise.promise(true);
|
|
}
|
|
}
|