177 lines
5.1 KiB
Haxe
177 lines
5.1 KiB
Haxe
package ru.m.storage;
|
|
|
|
import haxe.Unserializer;
|
|
import flash.net.SharedObject;
|
|
import haxe.crypto.Md5;
|
|
import haxe.io.Bytes;
|
|
import haxe.Serializer;
|
|
import promhx.Promise;
|
|
import ru.m.data.DataSource;
|
|
|
|
typedef DataMeta = Map<String, Dynamic>;
|
|
|
|
typedef IdMeta<I> = {
|
|
var id:I;
|
|
var meta:DataMeta;
|
|
}
|
|
|
|
interface DataHelper<D, I> {
|
|
public function idKey(id:I):String;
|
|
|
|
public function keyId(key:String):I;
|
|
|
|
public function dataId(data:D):I;
|
|
|
|
public function dataBytes(data:D):Bytes;
|
|
|
|
public function bytesData(bytes:Bytes):D;
|
|
|
|
public function dataMeta(data:D):DataMeta;
|
|
}
|
|
|
|
class DefaultHelper implements DataHelper<Bytes, String> {
|
|
public function new() {
|
|
|
|
}
|
|
|
|
public function idKey(id:String):String {
|
|
return id;
|
|
}
|
|
|
|
public function keyId(key:String):String {
|
|
return key;
|
|
}
|
|
|
|
public function dataId(data:Bytes):String {
|
|
return Md5.make(data).toHex();
|
|
}
|
|
|
|
public function dataBytes(data:Bytes):Bytes {
|
|
return data;
|
|
}
|
|
|
|
public function bytesData(bytes:Bytes):Bytes {
|
|
return bytes;
|
|
}
|
|
|
|
public function dataMeta(data:Bytes):DataMeta {
|
|
return ["date" => Date.now().toString()];
|
|
}
|
|
}
|
|
|
|
class SharedObjectDataStorage<D, I> implements DataStorage<D, I> {
|
|
private static var DATA_KEY:String = "data";
|
|
|
|
public var name(default, null):String;
|
|
public var version(default, null):Int;
|
|
|
|
private var helper:DataHelper<D, I>;
|
|
|
|
private var _indexObject:SharedObject;
|
|
private var indexObject(get, null):SharedObject;
|
|
|
|
private function get_indexObject():SharedObject {
|
|
if (_indexObject == null) {
|
|
_indexObject = SharedObject.getLocal('${name}/${version}/index');
|
|
}
|
|
return _indexObject;
|
|
}
|
|
|
|
public function new(name:String, helper:DataHelper<D, I>, version:Int = 1) {
|
|
this.name = name;
|
|
this.version = version;
|
|
this.helper = helper;
|
|
}
|
|
|
|
private function resolveDataObject(id:I):SharedObject {
|
|
var idKey = helper.idKey(id);
|
|
return SharedObject.getLocal('${name}/${version}/${Md5.encode(idKey)}');
|
|
}
|
|
|
|
private function applyFilter(filter:Null<Filter>, meta:DataMeta):Bool {
|
|
if (filter != null) {
|
|
for (k in filter.keys()) {
|
|
if (meta.exists(k) && meta.get(k) != filter.get(k)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private function applyOrder(order:Order, values:Array<IdMeta<I>>):Void {
|
|
if (order != null) {
|
|
values.sort((a:IdMeta<I>, b:IdMeta<I>) -> {
|
|
for (item in order) {
|
|
var av = a.meta.get(item.field);
|
|
var bv = b.meta.get(item.field);
|
|
if (av > bv) {
|
|
return item.reverse ? -1 : 1;
|
|
} else if (av < bv) {
|
|
return item.reverse ? 1 : -1;
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
public function getIndexPage(page:Page):Promise<DataPage<I>> {
|
|
var data = indexObject.data;
|
|
var values:Array<IdMeta<I>> = [];
|
|
for (k in Reflect.fields(data)) {
|
|
var meta = Unserializer.run(Reflect.field(data, k));
|
|
if (applyFilter(page.filter, meta)) {
|
|
values.push({id: helper.keyId(k), meta: meta});
|
|
}
|
|
}
|
|
applyOrder(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 dataObject = resolveDataObject(id);
|
|
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
|
return Promise.promise(helper.bytesData(Bytes.ofHex(Reflect.field(dataObject.data, DATA_KEY))));
|
|
}
|
|
return Promise.promise(null);
|
|
}
|
|
|
|
public function save(item:D):Promise<D> {
|
|
var id = helper.dataId(item);
|
|
var dataObject = resolveDataObject(id);
|
|
dataObject.setProperty(DATA_KEY, helper.dataBytes(item).toHex());
|
|
dataObject.flush();
|
|
indexObject.setProperty(helper.idKey(id), Serializer.run(helper.dataMeta(item)));
|
|
indexObject.flush();
|
|
return Promise.promise(item);
|
|
}
|
|
|
|
public function delete(id:I):Promise<Bool> {
|
|
var dataObject = resolveDataObject(id);
|
|
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
|
dataObject.clear();
|
|
return Promise.promise(true);
|
|
}
|
|
Reflect.deleteField(dataObject.data, helper.idKey(id));
|
|
return Promise.promise(false);
|
|
}
|
|
}
|