/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import java.util.ArrayList;
import org.objectfabric.InFlight.Provider;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.typedarrays.shared.ArrayBuffer;
final class IndexedDBView extends ArrayView {
private final String _uriKey;
IndexedDBView(Location location, String uriKey) {
super(location);
_uriKey = uriKey;
}
final String getKey(long tick) {
String tickKey = IndexedDB.getKey(tick);
return _uriKey + tickKey;
}
final long getTick(String key) {
return IndexedDB.getTick(key.substring(_uriKey.length()));
}
private final IndexedDB cache() {
return (IndexedDB) location();
}
@Override
final void getKnown(URI uri) {
long[] ticks = copy();
if (ticks != null) {
if (ticks.length != 0 || !location().isCache())
uri.onKnown(this, ticks);
} else
list(uri, null);
}
@Override
final void onKnown(URI uri, long[] ticks) {
if (isNull())
list(uri, ticks);
else
getUnknown(uri, ticks);
}
private final void list(final URI uri, final long[] compare) {
if (cache().db() == null) {
cache().runWhenStarted(new Runnable() {
@Override
public void run() {
listImpl(uri, compare);
}
});
} else
listImpl(uri, compare);
}
private final void listImpl(final URI uri, long[] compare) {
if (Debug.PERSISTENCE_LOG)
Log.write("IndexedDBView openCursor " + uri);
if (Stats.ENABLED)
Stats.Instance.BlockListCount.incrementAndGet();
ArrayList<String> keys = new ArrayList<String>();
cursor(uri, cache().db(), IndexedDB.BLOCKS, _uriKey, keys, compare);
}
private native void cursor(URI uri, JavaScriptObject db, String store, String key, ArrayList<String> keys, long[] compare) /*-{
var transaction = db.transaction(store);
var store = transaction.objectStore(store);
var cursor = store.openCursor(window.IDBKeyRange.lowerBound(key));
var this_ = this;
cursor.onsuccess = function(e) {
var result = e.target.result;
var key = null;
if (result)
key = result.key;
var done = this_.@org.objectfabric.IndexedDBView::step(Lorg/objectfabric/URI;Ljava/lang/String;Ljava/util/ArrayList;[J)(uri, key, keys, compare);
if (!done)
result["continue"]();
};
}-*/;
@SuppressWarnings("null")
private final boolean step(URI uri, String key, ArrayList<String> keys, long[] compare) {
boolean done = key == null;
if (!done)
for (int i = 0; i < SHA1Digest.LENGTH / 2; i++)
if (key.charAt(i) != _uriKey.charAt(i))
done = true;
if (!done)
keys.add(key);
else {
long[] ticks = null;
if (keys.size() > 0) {
for (int i = 0; i < keys.size(); i++)
ticks = Tick.add(ticks, getTick(keys.get(i)));
} else
ticks = Tick.EMPTY;
onLoad(uri, ticks, compare);
}
return done;
}
@Override
void getBlock(URI uri, long tick) {
if (Debug.PERSISTENCE_LOG)
Log.write("IndexedDB read " + uri + " - " + Tick.toString(tick));
Read read = read(cache().db(), IndexedDB.BLOCKS, getKey(tick), uri, tick);
if (!InFlight.starting(uri, tick, read))
read.cancel(uri, tick);
}
private native Read read(JavaScriptObject db, String store, String key, URI uri, Long tick) /*-{
var transaction = db.transaction(store);
var request = transaction.objectStore(store).get(key);
var this_ = this;
request.onsuccess = function(e) {
this_.@org.objectfabric.IndexedDBView::onsuccess(Lorg/objectfabric/URI;Ljava/lang/Long;Lcom/google/gwt/typedarrays/shared/ArrayBuffer;)(uri, tick, e.target.result);
};
return transaction;
}-*/;
private final void onsuccess(URI uri, Long tick, ArrayBuffer buffer) {
GWTBuff buff = new GWTBuff(Uint8Array.create(buffer));
if (Debug.ENABLED)
buff.lock(buff.limit());
if (Stats.ENABLED)
Stats.Instance.BlockReadCount.incrementAndGet();
Buff[] buffs = new Buff[] { buff };
Exception exception = uri.onBlock(this, tick, buffs, null, true, null, false, null);
if (exception != null) {
// TODO make sure exception is related to parsing
Log.write("Corrupted block " + exception.toString());
// TODO Make option or callback to clean corrupted
// file.delete();
}
}
static final class Read extends JavaScriptObject implements Provider {
protected Read() {
}
@Override
public void cancel(URI uri, long tick) {
cancel(this);
}
private native void cancel(Read read)/*-{
read.abort();
}-*/;
}
@Override
final void onBlock(URI uri, long tick, Buff[] buffs, long[] removals, boolean requested) {
cache().queue().enqueueBlock(uri, tick, buffs, removals, requested);
}
}