package edu.brown.utils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.json.*;
import org.voltdb.VoltType;
import org.voltdb.catalog.CatalogType;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Table;
import org.voltdb.utils.VoltTypeUtil;
import edu.brown.BaseTestCase;
import edu.brown.catalog.CatalogKey;
import edu.brown.hashing.AbstractHasher;
import edu.brown.hashing.DefaultHasher;
import edu.brown.utils.TestJSONUtil.TestObject.TestEnum;
public class TestJSONUtil extends BaseTestCase {
public static class TestObject implements JSONSerializable {
public enum Members {
DATA_INT,
DATA_INT_OBJ,
DATA_LONG,
DATA_LONG_OBJ,
DATA_DOUBLE,
DATA_DOUBLE_OBJ,
DATA_BOOLEAN,
DATA_BOOLEAN_OBJ,
DATA_ENUM,
LIST_INT,
LIST_LONG,
LIST_DOUBLE,
LIST_BOOLEAN,
LIST_CATALOG,
LIST_ENUM,
LIST_NULL,
SET_INT,
SET_LONG,
SET_DOUBLE,
SET_BOOLEAN,
SET_CATALOG,
SET_NULL,
MAP_INT,
MAP_LONG,
MAP_DOUBLE,
MAP_STRING,
MAP_CATALOG,
MAP_NULL,
SPECIAL_CLASS,
SPECIAL_CATALOG,
}
public static final Set<Members> PRIMITIVES = new ListOrderedSet<Members>();
public static final Set<Members> LISTS = new ListOrderedSet<Members>();
public static final Set<Members> MAPS = new ListOrderedSet<Members>();
public static final Set<Members> SPECIALS = new ListOrderedSet<Members>();
static {
for (Members e : Members.values()) {
String name = e.name();
if (name.startsWith("DATA")) {
PRIMITIVES.add(e);
} else if (name.startsWith("LIST") || name.startsWith("SET")) {
LISTS.add(e);
} else if (name.startsWith("MAP")) {
MAPS.add(e);
} else if (name.startsWith("SPECIAL")) {
SPECIALS.add(e);
} else {
assert(false) : "Unexpected Member '" + e + "'";
}
} // FOR
assert(!PRIMITIVES.isEmpty()) : "No primitive members selected";
assert(!LISTS.isEmpty()) : "No list members selected";
assert(!MAPS.isEmpty()) : "No map members selected";
assert(!SPECIALS.isEmpty()) : "No map members selected";
}
// --------------------------------------------------------------------------------
// Data Members
// --------------------------------------------------------------------------------
// Enum
public enum TestEnum {
CLUBS,
HEARTS,
DIAMONDS,
SPADES,
}
// Primitives
public int data_int;
public Integer data_int_obj;
public long data_long;
public Long data_long_obj;
public double data_double;
public Double data_double_obj;
public boolean data_boolean;
public Boolean data_boolean_obj;
public TestEnum data_enum;
// Lists
public List<Integer> list_int = new ArrayList<Integer>();
public List<Long> list_long = new ArrayList<Long>();
public List<Double> list_double = new ArrayList<Double>();
public List<Boolean> list_boolean = new ArrayList<Boolean>();
public List<Table> list_catalog = new ArrayList<Table>();
public List<TestEnum> list_enum = new ArrayList<TestEnum>();
public List<String> list_null = new ArrayList<String>();
public Set<Integer> set_int = new HashSet<Integer>();
public Set<Long> set_long = new HashSet<Long>();
public Set<Double> set_double = new HashSet<Double>();
public Set<Boolean> set_boolean = new HashSet<Boolean>();
public Set<Table> set_catalog = new HashSet<Table>();
public Set<TestEnum> set_enum = new HashSet<TestEnum>();
public Set<String> set_null = new HashSet<String>();
// Maps
public Map<Integer, String> map_int = new HashMap<Integer, String>();
public Map<Long, String> map_long = new HashMap<Long, String>();
public Map<Double, String> map_double = new HashMap<Double, String>();
public Map<String, String> map_string = new HashMap<String, String>();
public Map<Table, String> map_catalog = new HashMap<Table, String>();
public Map<TestEnum, String> map_enum = new HashMap<TestEnum, String>();
public Map<String, String> map_null = new HashMap<String, String>();
// Specials
public Class<? extends AbstractHasher> special_class;
public Procedure special_catalog;
// --------------------------------------------------------------------------------
// JSONSerializable Methods
// --------------------------------------------------------------------------------
@Override
public void load(File input_path, Database catalog_db) throws IOException {
JSONUtil.load(this, catalog_db, input_path);
}
@Override
public void save(File output_path) throws IOException {
JSONUtil.save(this, output_path);
}
@Override
public void fromJSON(JSONObject json_object, Database catalog_db) throws JSONException {
this.fromJSON(json_object, catalog_db, Arrays.asList(Members.values()));
}
@Override
public void toJSON(JSONStringer stringer) throws JSONException {
this.toJSON(stringer, Arrays.asList(Members.values()));
}
@Override
public String toJSONString() {
return (this.toJSONString(Arrays.asList(Members.values())));
}
public void fromJSON(JSONObject json_object, Database catalog_db, Collection<Members> members) throws JSONException {
Members members_arr[] = new Members[members.size()];
members.toArray(members_arr);
JSONUtil.fieldsFromJSON(json_object, catalog_db, this, TestObject.class, members_arr);
}
public void toJSON(JSONStringer stringer, Collection<Members> members) throws JSONException {
Members members_arr[] = new Members[members.size()];
members.toArray(members_arr);
JSONUtil.fieldsToJSON(stringer, this, TestObject.class, members_arr);
}
public String toJSONString(Collection<Members> members) {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
this.toJSON(stringer, members);
stringer.endObject();
} catch (JSONException e) {
e.printStackTrace();
System.exit(-1);
}
return (stringer.toString());
}
}
private final Random rand = new Random(0);
private TestObject obj;
@Override
protected void setUp() throws Exception {
super.setUp(ProjectType.TM1);
this.obj = new TestObject();
this.obj.data_int = rand.nextInt();
this.obj.data_int_obj = new Integer(rand.nextInt());
this.obj.data_long = rand.nextLong();
this.obj.data_long_obj = new Long(rand.nextLong());
this.obj.data_double = rand.nextDouble();
this.obj.data_double_obj = new Double(rand.nextDouble());
this.obj.data_boolean = rand.nextBoolean();
this.obj.data_boolean_obj = new Boolean(rand.nextBoolean());
this.obj.data_enum = TestEnum.values()[rand.nextInt(TestEnum.values().length)];
List<Table> tables = CollectionUtil.list(catalog_db.getTables());
for (int i = 0, cnt = rand.nextInt(20) + 1; i < cnt; i++) {
this.obj.list_int.add(rand.nextInt());
this.obj.list_long.add(rand.nextLong());
this.obj.list_double.add(rand.nextDouble());
this.obj.list_boolean.add(rand.nextBoolean());
this.obj.list_catalog.add(CollectionUtil.random(tables));
this.obj.list_enum.add(TestEnum.values()[rand.nextInt(TestEnum.values().length)]);
this.obj.list_null.add(i % 2 == 0 ? VoltTypeUtil.getRandomValue(VoltType.STRING).toString() : null);
this.obj.set_int.add(rand.nextInt());
this.obj.set_long.add(rand.nextLong());
this.obj.set_double.add(rand.nextDouble());
this.obj.set_boolean.add(rand.nextBoolean());
this.obj.set_catalog.add(CollectionUtil.random(tables));
this.obj.set_enum.add(TestEnum.values()[rand.nextInt(TestEnum.values().length)]);
this.obj.set_null.add(i % 2 == 0 ? VoltTypeUtil.getRandomValue(VoltType.STRING).toString() : null);
this.obj.map_int.put(rand.nextInt(), VoltTypeUtil.getRandomValue(VoltType.STRING).toString());
this.obj.map_long.put(rand.nextLong(), VoltTypeUtil.getRandomValue(VoltType.STRING).toString());
this.obj.map_double.put(rand.nextDouble(), VoltTypeUtil.getRandomValue(VoltType.STRING).toString());
this.obj.map_string.put(VoltTypeUtil.getRandomValue(VoltType.STRING).toString(), VoltTypeUtil.getRandomValue(VoltType.STRING).toString());
this.obj.map_catalog.put(CollectionUtil.random(tables), VoltTypeUtil.getRandomValue(VoltType.STRING).toString());
this.obj.map_enum.put(TestEnum.values()[rand.nextInt(TestEnum.values().length)], VoltTypeUtil.getRandomValue(VoltType.STRING).toString());
this.obj.map_null.put(VoltTypeUtil.getRandomValue(VoltType.STRING).toString(),
i % 2 == 0 ? VoltTypeUtil.getRandomValue(VoltType.STRING).toString() : null);
} // FOR
this.obj.special_class = DefaultHasher.class;
this.obj.special_catalog = this.getProcedure("GetNewDestination");
}
private JSONObject toJSONObject(TestObject orig, Collection<TestObject.Members> members) throws Exception {
String json_string = orig.toJSONString(members);
assert(json_string.length() > 0);
JSONObject json_object = new JSONObject(json_string);
assertNotNull(json_object);
// if (members.size() == 2) System.err.println(json_object.toString(1));
assertEquals(members.size(), json_object.length());
return (json_object);
}
private TestObject clone(TestObject orig, Collection<TestObject.Members> members) throws Exception {
assert(!members.isEmpty());
JSONObject json_object = new JSONObject(orig.toJSONString());
TestObject clone = new TestObject();
clone.fromJSON(json_object, catalog_db, members);
return (clone);
}
// --------------------------------------------------------------------------------
// Test Cases
// --------------------------------------------------------------------------------
/**
* testPrimitiveFieldsToJSON
*/
public void testPrimitiveFieldsToJSON() throws Exception {
JSONObject json_object = this.toJSONObject(obj, TestObject.PRIMITIVES);
for (TestObject.Members e : TestObject.PRIMITIVES) {
String key = e.name();
assert(json_object.has(key));
String value = json_object.getString(key);
assert(value.length() > 0);
Field field = TestObject.class.getField(key.toLowerCase());
assertNotNull(field);
assertEquals(field.get(obj).toString(), value);
} // FOR
}
/**
* testPrimitiveFieldsFromJSON
*/
public void testPrimitiveFieldsFromJSON() throws Exception {
TestObject clone = this.clone(obj, TestObject.PRIMITIVES);
for (TestObject.Members e : TestObject.PRIMITIVES) {
String key = e.name();
Field field = TestObject.class.getField(key.toLowerCase());
assertNotNull(field);
assertEquals(field.get(obj), field.get(clone));
} // FOR
}
/**
* testListFieldsToJSON
*/
@SuppressWarnings("unchecked")
public void testListFieldsToJSON() throws Exception {
JSONObject json_object = this.toJSONObject(obj, TestObject.LISTS);
for (TestObject.Members e : TestObject.LISTS) {
String json_key = e.name();
assert(json_object.has(json_key));
Field field = TestObject.class.getField(json_key.toLowerCase());
assertNotNull(field);
Collection collection = (Collection)field.get(obj);
List<String> collection_strings = new ArrayList<String>();
for (Object o : collection) {
if (o instanceof CatalogType) {
collection_strings.add(CatalogKey.createKey((CatalogType)o));
} else {
collection_strings.add(o != null ? o.toString() : "null");
}
} // FOR
JSONArray json_array = json_object.getJSONArray(json_key);
assertEquals(collection.size(), json_array.length());
for (int i = 0, cnt = json_array.length(); i < cnt; i++) {
String value = json_array.getString(i);
assert(collection_strings.contains(value)) :
"Missing element '" + value + "' from field " + e + ": " + collection_strings;
} // FOR
} // FOR
}
/**
* testListFieldsFromJSON
*/
@SuppressWarnings("unchecked")
public void testListFieldsFromJSON() throws Exception {
TestObject clone = this.clone(obj, TestObject.LISTS);
for (TestObject.Members e : TestObject.LISTS) {
String json_key = e.name();
Field field = TestObject.class.getField(json_key.toLowerCase());
assertNotNull(field);
Collection collection0 = (Collection)field.get(obj);
Collection collection1 = (Collection)field.get(clone);
assertEquals(collection0.size(), collection1.size());
assert(collection0.containsAll(collection1));
} // FOR
}
/**
* testMapsToJSON
*/
public void testMapsToJSON() throws Exception {
// JSONObject json_object = this.toJSONObject(obj, TestObject.MAPS);
for (TestObject.Members e : TestObject.MAPS) {
String json_key = e.name();
// assert(json_object.has(json_key));
Field field = TestObject.class.getField(json_key.toLowerCase());
assertNotNull(field);
Map<?,?> map = (Map<?,?>)field.get(obj);
String json_string = JSONUtil.toJSONString(map);
JSONObject json_object = new JSONObject(json_string);
assertNotNull(json_object);
// System.err.println(field.getName() + " -> " + json_string);
List<String> key_strings = new ArrayList<String>();
List<String> val_strings = new ArrayList<String>();
for (Object key : map.keySet()) {
Object val = map.get(key);
if (key instanceof CatalogType) {
key_strings.add(CatalogKey.createKey((CatalogType)key));
} else {
key_strings.add(key != null ? key.toString() : "null");
}
val_strings.add(val != null ? val.toString() : "null");
} // FOR
assertEquals(map.size(), key_strings.size());
assertEquals(map.size(), val_strings.size());
// JSONObject json_inner_obj = json_object.getJSONObject(json_key);
Iterator<String> json_keys_it = json_object.keys();
while (json_keys_it.hasNext()) {
String json_inner_key = json_keys_it.next();
assert(!json_inner_key.isEmpty());
String json_inner_val = json_object.getString(json_inner_key);
assert(key_strings.contains(json_inner_key));
int idx = key_strings.indexOf(json_inner_key);
assertEquals(val_strings.get(idx), json_inner_val);
} // WHILE
} // FOR
}
/**
* testMapFieldsToJSON
*/
public void testMapFieldsToJSON() throws Exception {
JSONObject json_object = this.toJSONObject(obj, TestObject.MAPS);
for (TestObject.Members e : TestObject.MAPS) {
String json_key = e.name();
assert(json_object.has(json_key));
Field field = TestObject.class.getField(json_key.toLowerCase());
assertNotNull(field);
Map<?,?> map = (Map<?,?>)field.get(obj);
List<String> key_strings = new ArrayList<String>();
List<String> val_strings = new ArrayList<String>();
for (Object key : map.keySet()) {
Object val = map.get(key);
if (key instanceof CatalogType) {
key_strings.add(CatalogKey.createKey((CatalogType)key));
} else {
key_strings.add(key != null ? key.toString() : "null");
}
val_strings.add(val != null ? val.toString() : "null");
} // FOR
assertEquals(map.size(), key_strings.size());
assertEquals(map.size(), val_strings.size());
JSONObject json_inner_obj = json_object.getJSONObject(json_key);
Iterator<String> json_keys_it = json_inner_obj.keys();
while (json_keys_it.hasNext()) {
String json_inner_key = json_keys_it.next();
assert(!json_inner_key.isEmpty());
String json_inner_val = json_inner_obj.getString(json_inner_key);
assert(key_strings.contains(json_inner_key));
int idx = key_strings.indexOf(json_inner_key);
assertEquals(val_strings.get(idx), json_inner_val);
} // WHILE
} // FOR
}
/**
* testMapFieldsFromJSON
*/
@SuppressWarnings("unchecked")
public void testMapFieldsFromJSON() throws Exception {
TestObject clone = this.clone(obj, TestObject.MAPS);
for (TestObject.Members e : TestObject.MAPS) {
String json_key = e.name();
Field field = TestObject.class.getField(json_key.toLowerCase());
assertNotNull(field);
Map m0 = (Map)field.get(obj);
Map m1 = (Map)field.get(clone);
assertEquals(m0.size(), m1.size());
assert(m0.keySet().containsAll(m1.keySet()));
assert(m1.keySet().containsAll(m0.keySet()));
for (Object key : m0.keySet()) {
Object v0 = m0.get(key);
Object v1 = m1.get(key);
final String debug = String.format("%s-%s: %s (%s) <=> %s (%s)", json_key,
(key != null ? key.toString() : key),
(v0 != null ? v0.toString() : v0), (v0 != null ? v0.getClass().getSimpleName() : null),
(v1 != null ? v1.toString() : v1), (v1 != null ? v1.getClass().getSimpleName() : null));
try {
assertEquals(debug, v0, v1);
} catch (AssertionError ex) {
System.err.println(JSONUtil.format(obj));
throw ex;
}
} // FOR
} // FOR
}
/**
* testSpecialFieldsToJSON
*/
@SuppressWarnings("unchecked")
public void testSpecialFieldsToJSON() throws Exception {
JSONObject json_object = this.toJSONObject(obj, TestObject.SPECIALS);
for (TestObject.Members e : TestObject.SPECIALS) {
String json_key = e.name();
assert(json_object.has(json_key));
String json_value = json_object.getString(json_key);
assert(json_value.length() > 0);
Field field = TestObject.class.getField(json_key.toLowerCase());
assertNotNull(field);
Object field_value = field.get(obj);
Class<?> field_class = field.getType();
switch (e) {
case SPECIAL_CLASS:
assertEquals(((Class<?>)field_value).getName(), json_value);
break;
case SPECIAL_CATALOG:
assertEquals(((CatalogType)field_value).getPath(), CatalogKey.getFromKey(catalog_db, json_value, (Class<? extends CatalogType>)field_class).getPath());
break;
default:
assert(false) : "Unexpected field '" + json_key + "'";
} // SWITCH
} // FOR
}
/**
* testSpecialFieldsFromJSON
*/
public void testSpecialFieldsFromJSON() throws Exception {
TestObject clone = this.clone(obj, TestObject.SPECIALS);
for (TestObject.Members e : TestObject.SPECIALS) {
String key = e.name();
Field field = TestObject.class.getField(key.toLowerCase());
assertNotNull(field);
assertEquals(field.get(obj), field.get(clone));
} // FOR
}
}