package org.jboss.errai.jpa.client.local.backend;
import java.util.ArrayList;
import java.util.List;
import org.jboss.errai.common.client.api.Assert;
import org.jboss.errai.jpa.client.local.EntityJsonMatcher;
import org.jboss.errai.jpa.client.local.ErraiEntityManager;
import org.jboss.errai.jpa.client.local.ErraiEntityType;
import org.jboss.errai.jpa.client.local.JsonUtil;
import org.jboss.errai.jpa.client.local.Key;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
/**
* The storage backend for HTML WebStorage, a storage facility supported by most
* browsers for at least 2.5 million characters of data, (5 megabytes of unicode text).
*
* @author Jonathan Fuerth <jfuerth@gmail.com>
*/
public class WebStorageBackend implements StorageBackend {
private final ErraiEntityManager em;
public WebStorageBackend(ErraiEntityManager erraiEntityManager) {
em = Assert.notNull(erraiEntityManager);
}
private native void putImpl(String key, String value) /*-{
$wnd.localStorage.setItem(key, value);
}-*/;
private native String getImpl(String key) /*-{
return $wnd.localStorage.getItem(key);
}-*/;
private native String removeImpl(String key) /*-{
return $wnd.localStorage.removeItem(key);
}-*/;
@Override
public native void removeAll() /*-{
for (var i = $wnd.localStorage.length - 1; i >= 0; i--) {
var key = $wnd.localStorage.key(i);
$wnd.localStorage.removeItem(key);
}
}-*/;
/**
* Invokes the given entry visitor on each key/value pair in this entire
* storage backend.
*
* @param entryVisitor
* The visitor that will act on each key/value pair.
*/
private native void forEachKey(EntryVisitor entryVisitor) /*-{
for (var i = 0, n = $wnd.localStorage.length; i < n; i++) {
var key = $wnd.localStorage.key(i);
var value = $wnd.localStorage.getItem(key);
entryVisitor.@org.jboss.errai.jpa.client.local.backend.EntryVisitor::visit(Ljava/lang/String;Ljava/lang/String;)(key, value);
}
}-*/;
@Override
public <X> void put(Key<X,?> key, X value) {
ErraiEntityType<X> entityType = key.getEntityType();
String keyJson = key.toJson();
JSONValue valueJson = entityType.toJson(em, value);
System.out.println(">>>put '" + keyJson + "'");
putImpl(keyJson, valueJson.toString());
}
@Override
public <X> X get(Key<X, ?> key) {
ErraiEntityType<X> entityType = key.getEntityType();
String keyJson = key.toJson();
String valueJson = getImpl(keyJson);
System.out.println("<<<get '" + keyJson + "' : " + valueJson);
X entity;
if (valueJson == null) {
entity = null;
}
else {
entity = entityType.fromJson(em, JSONParser.parseStrict(valueJson));
}
System.out.println(" returning " + entity);
return entity;
}
@Override
public <X> List<X> getAll(final ErraiEntityType<X> type, final EntityJsonMatcher matcher) {
// TODO index entries by entity type
final List<X> entities = new ArrayList<X>();
forEachKey(new EntryVisitor() {
@Override
public void visit(String key, String value) {
Key<?, ?> k = Key.fromJson(em, key, false);
if (k == null) return;
System.out.println("getAll(): considering " + value);
if (k.getEntityType() == type) {
System.out.println(" --> correct type");
JSONObject candidate = JSONParser.parseStrict(value).isObject();
Assert.notNull(candidate);
if (matcher.matches(candidate)) {
@SuppressWarnings("unchecked")
Key<X, ?> typedKey = (Key<X, ?>) k;
// Unfortunately, this throws away a lot of work we've already done (getting the entity type,
// creating the key, doing a backend.get(), parsing the JSON value, ...)
// it would be nice to avoid this, but we have to go back to the entity manager in case the
// thing we want is in the persistence context.
entities.add(em.find(type.getJavaType(), typedKey.getId()));
}
else {
System.out.println(" --> but not a match");
}
}
else {
System.out.println(" --> wrong type");
}
}
});
return entities;
}
@Override
public boolean contains(Key<?, ?> key) {
String keyJson = key.toJson();
boolean contains = getImpl(keyJson) != null;
System.out.println("<<<contains '" + keyJson + "' : " + contains);
return contains;
}
@Override
public <X> void remove(Key<X, ?> key) {
String keyJson = key.toJson();
removeImpl(keyJson);
}
@Override
public <X> boolean isModified(Key<X, ?> key, X value) {
ErraiEntityType<X> entityType = key.getEntityType();
String keyJson = key.toJson();
JSONValue newValueJson = entityType.toJson(em, value);
JSONValue oldValueJson = JSONParser.parseStrict(getImpl(keyJson));
boolean modified = !JsonUtil.equals(newValueJson, oldValueJson);
if (modified) {
System.out.println("Detected modified entity " + key);
System.out.println(" Old: " + oldValueJson);
System.out.println(" New: " + newValueJson);
}
return modified;
}
}