Jackson serialization for preferences

This commit is contained in:
Antonin Delpeuch 2018-09-29 18:09:41 +01:00
parent 418b21dda2
commit 18c2183cbc
4 changed files with 79 additions and 6 deletions

View File

@ -44,6 +44,7 @@ import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.refine.Jsonizable; import com.google.refine.Jsonizable;
import com.google.refine.browsing.facets.Facet; import com.google.refine.browsing.facets.Facet;
@ -72,6 +73,7 @@ public class Engine implements Jsonizable {
protected Project _project; protected Project _project;
protected List<Facet> _facets = new LinkedList<Facet>(); protected List<Facet> _facets = new LinkedList<Facet>();
@JsonValue
protected EngineConfig _config = new EngineConfig(Collections.emptyList(), Mode.RowBased); protected EngineConfig _config = new EngineConfig(Collections.emptyList(), Mode.RowBased);
static public String modeToString(Mode mode) { static public String modeToString(Mode mode) {

View File

@ -33,6 +33,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.refine.preference; package com.google.refine.preference;
import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -40,10 +41,18 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.refine.Jsonizable; import com.google.refine.Jsonizable;
import com.google.refine.RefineServlet; import com.google.refine.RefineServlet;
@ -54,21 +63,40 @@ public class PreferenceStore implements Jsonizable {
public static final String USER_NAME = "username"; public static final String USER_NAME = "username";
private boolean dirty = false; private boolean dirty = false;
protected Map<String, Object> _prefs = new HashMap<String, Object>(); protected Map<String, Object> _prefs = new HashMap<>();
// Temporary wrapper while serialization has not been migrated yet.
@JsonProperty("entries")
protected Map<String, Object> _prefsJackson = new HashMap<>();
public void put(String key, Object value) { public void put(String key, Object value) {
if (value == null) { if (value == null) {
_prefs.remove(key); _prefs.remove(key);
_prefsJackson.remove(key);
} else { } else {
_prefs.put(key, value); _prefs.put(key, value);
_prefsJackson.put(key, wrapJSONArray(value));
} }
dirty = true; dirty = true;
} }
private Object wrapJSONArray(Object value) {
ObjectMapper mapper = new ObjectMapper();
if(value != null && value instanceof JSONArray) {
try {
return mapper.readValue(value.toString(), JsonNode.class);
} catch (IOException e) {
return null;
}
}
return value;
}
public Object get(String key) { public Object get(String key) {
return _prefs.get(key); return _prefs.get(key);
} }
@JsonIgnore
public Set<String> getKeys() { public Set<String> getKeys() {
return _prefs.keySet(); return _prefs.keySet();
} }
@ -98,10 +126,24 @@ public class PreferenceStore implements Jsonizable {
/** /**
* @return true if the preference store has unsaved changes * @return true if the preference store has unsaved changes
*/ */
@JsonIgnore
public boolean isDirty() { public boolean isDirty() {
return dirty; return dirty;
} }
/**
* Mark the object as clean every time it is serialized.
* This behaviour is not very clean - it is inherited from
* the previous deserialization code.
* @return
*/
@JsonProperty("makeClean")
@JsonInclude(Include.NON_NULL)
public Integer markAsClean() {
dirty = false;
return null;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void load(JSONObject obj) throws JSONException { public void load(JSONObject obj) throws JSONException {
if (obj.has("entries") && !obj.isNull("entries")) { if (obj.has("entries") && !obj.isNull("entries")) {
@ -111,8 +153,9 @@ public class PreferenceStore implements Jsonizable {
while (i.hasNext()) { while (i.hasNext()) {
String key = i.next(); String key = i.next();
if (!entries.isNull(key)) { if (!entries.isNull(key)) {
Object o = entries.get(key); Object o = entries.get(key), loaded = loadObject(o);
_prefs.put(key, loadObject(o)); _prefs.put(key, loaded);
_prefsJackson.put(key, wrapJSONArray(loaded));
} }
} }
dirty = false; // internal puts don't count dirty = false; // internal puts don't count

View File

@ -44,10 +44,14 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.refine.Jsonizable; import com.google.refine.Jsonizable;
public class TopList implements Jsonizable, Iterable<String> { public class TopList implements Jsonizable, Iterable<String> {
@JsonProperty("top")
final protected int _top; final protected int _top;
final protected List<String> _list = new ArrayList<String>(); final protected List<String> _list = new ArrayList<String>();
@ -56,6 +60,7 @@ public class TopList implements Jsonizable, Iterable<String> {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@JsonProperty("list")
public List<String> getList() { public List<String> getList() {
return UnmodifiableList.decorate(_list); return UnmodifiableList.decorate(_list);
} }
@ -72,11 +77,16 @@ public class TopList implements Jsonizable, Iterable<String> {
{ {
_list.remove(element); _list.remove(element);
} }
@JsonProperty("class")
public String getClassName() {
return this.getClass().getName();
}
@Override @Override
public void write(JSONWriter writer, Properties options) throws JSONException { public void write(JSONWriter writer, Properties options) throws JSONException {
writer.object(); writer.object();
writer.key("class"); writer.value(this.getClass().getName()); writer.key("class"); writer.value(getClassName());
writer.key("top"); writer.value(_top); writer.key("top"); writer.value(_top);
writer.key("list"); writer.key("list");
@ -107,7 +117,8 @@ public class TopList implements Jsonizable, Iterable<String> {
} }
} }
@Override @Override
@JsonIgnore
public Iterator<String> iterator() { public Iterator<String> iterator() {
return _list.iterator(); return _list.iterator();
} }

View File

@ -1,5 +1,8 @@
package com.google.refine.tests.preference; package com.google.refine.tests.preference;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import org.json.JSONObject; import org.json.JSONObject;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -17,8 +20,22 @@ public class PreferenceStoreTests {
+ " \"scripting.starred-expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":2147483647,\"list\":[]}," + " \"scripting.starred-expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":2147483647,\"list\":[]},"
+ " \"scripting.expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":100,\"list\":[]}" + " \"scripting.expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":100,\"list\":[]}"
+ "}}"; + "}}";
String jsonAfter = "{"
+ "\"entries\":{"
+ " \"reconciliation.standardServices\":["
+ " {\"propose_properties\":{\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/propose_properties\"},\"preview\":{\"width\":320,\"url\":\"https://tools.wmflabs.org/openrefine-wikidata/en/preview?id={{id}}\",\"height\":90},\"view\":{\"url\":\"https://www.wikidata.org/wiki/{{id}}\"},\"ui\":{\"handler\":\"ReconStandardServicePanel\"},\"identifierSpace\":\"http://www.wikidata.org/entity/\",\"name\":\"Wikidata Reconciliation for OpenRefine (en)\",\"suggest\":{\"property\":{\"flyout_service_path\":\"/en/flyout/property?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/property\"},\"type\":{\"flyout_service_path\":\"/en/flyout/type?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/type\"},\"entity\":{\"flyout_service_path\":\"/en/flyout/entity?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/entity\"}},\"defaultTypes\":[{\"name\":\"entity\",\"id\":\"Q35120\"}],\"url\":\"https://tools.wmflabs.org/openrefine-wikidata/en/api\",\"schemaSpace\":\"http://www.wikidata.org/prop/direct/\"}"
+ " ],"
+ " \"scripting.starred-expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":2147483647,\"list\":[]},"
+ " \"scripting.expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":100,\"list\":[]},"
+ " \"mypreference\":\"myvalue\""
+ "}}";
PreferenceStore prefStore = new PreferenceStore(); PreferenceStore prefStore = new PreferenceStore();
prefStore.load(new JSONObject(json)); prefStore.load(new JSONObject(json));
TestUtils.isSerializedTo(prefStore, json); assertFalse(prefStore.isDirty());
prefStore.put("mypreference", "myvalue");
assertTrue(prefStore.isDirty());
TestUtils.isSerializedTo(prefStore, jsonAfter);
assertFalse(prefStore.isDirty());
} }
} }