package org.witness.informacam.models;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.witness.informacam.InformaCam;
import org.witness.informacam.json.JSONArray;
import org.witness.informacam.json.JSONException;
import org.witness.informacam.json.JSONObject;
import org.witness.informacam.json.JSONTokener;
import org.witness.informacam.utils.Constants.App;
import org.witness.informacam.utils.Constants.App.Informa;
import android.util.Log;
public class Model extends JSONObject {
public final static String LOG = App.LOG;
Field[] fields;
public void inflate(byte[] jsonStringBytes) throws InstantiationException, IllegalAccessException {
try {
if(jsonStringBytes != null) {
inflate((JSONObject) new JSONTokener(new String(jsonStringBytes)).nextValue());
} else {
Log.d(LOG, "json is null, no inflate");
}
} catch (JSONException e) {
Log.e(LOG, e.toString());
e.printStackTrace();
} catch(NullPointerException e) {
Log.e(LOG, e.toString());
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
public Class<?> recast(Object m, JSONObject ja) {
InformaCam informaCam = InformaCam.getInstance();
Set<Map<Class<?>, List<String>>> subclasses = new HashSet<Map<Class<?>, List<String>>>();
Class<?> clz = m.getClass();
Class<?> recast = null;
String packagePath = clz.getName().replace(("." + clz.getSimpleName()), "");
for(String model : informaCam.models) {
if(model.contains(packagePath) && !model.equals(clz.getName())) {
try {
Class<?> subClz = Class.forName(model);
if(subClz.getSuperclass().equals(clz)) {
//Log.d(LOG, "adding " + model + " as possible subclass for " + clz.getName());
List<String> fieldSet = new ArrayList<String>();
for(Field subField : subClz.getDeclaredFields()) {
if(subField.getModifiers() == Field.DECLARED) {
fieldSet.add(subField.getName());
}
}
Map<Class<?>, List<String>> subClz_ = new HashMap<Class<?>, List<String>>();
subClz_.put(subClz, fieldSet);
subclasses.add(subClz_);
}
} catch (ClassNotFoundException e) {
Log.e(LOG, e.toString());
e.printStackTrace();
}
}
}
if(subclasses.size() > 0) {
// loop through json to see if we have any of these fields. eliminate non-matches from list
Iterator<String> kIt = ja.keys();
while(kIt.hasNext()) {
String keyToFind = kIt.next();
int keyFoundInClasses = 0;
Class<?> c = null;
for(Map<Class<?>, List<String>> subClz : subclasses) {
// does property in key set belong exclusively to
Entry<Class<?>, List<String>> entry = subClz.entrySet().iterator().next();
List<String> ls = entry.getValue();
//Log.d(LOG, "parsing " + entry.getKey().getName() + " to see if it contains " + keyToFind);
if(ls.contains(keyToFind)) {
keyFoundInClasses++;
c = entry.getKey();
}
}
//Log.d(LOG, "found " + keyToFind + " in " + keyFoundInClasses + " class(es)");
if(keyFoundInClasses == 1) {
//Log.d(LOG, "downcast object to " + c.getName());
return c;
}
}
}
return recast;
}
public void inflate(Model model) throws InstantiationException, IllegalAccessException {
inflate(model.asJson());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void inflate(JSONObject values) throws InstantiationException, IllegalAccessException {
// Log.d(Informa.LOG,"Inflating model: " + getClass().getName() + " with values: " + values.toString());
fields = this.getClass().getFields();
for(Field f : fields) {
f.setAccessible(true);
if(values.has(f.getName())) {
boolean isModel = false;
// Log.d(Informa.LOG,"inflating field: " + f.getName() + " " + f.getType().getName());
if(f.getType().getSuperclass() == Model.class) {
isModel = true;
}
if(f.getType() == List.class) {
List subValue = new ArrayList();
Class clz = (Class<?>) ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0];
Object test = clz.newInstance();
if(test instanceof Model) {
isModel = true;
}
JSONArray ja = values.getJSONArray(f.getName());
for(int i=0; i<ja.length(); i++) {
Object value = clz.newInstance();
if(isModel) {
Class<?> recast = recast(value, ja.getJSONObject(i));
if(recast != null) {
value = recast.newInstance();
}
((Model) value).inflate(ja.getJSONObject(i));
} else {
value = ja.get(i);
}
subValue.add(value);
}
f.set(this, subValue);
} else if(f.getType() == byte[].class) {
f.set(this, values.getString(f.getName()).getBytes());
} else if(f.getType() == float[].class) {
//f.set(this, parseJSONAsFloatArray(values.getString(f.getName())));
f.set(this, parseJSONAsFloatArray(values.getJSONArray(f.getName()).toString()));
} else if(f.getType() == int[].class) {
f.set(this, parseJSONAsIntArray(values.getJSONArray(f.getName()).toString()));
} else if(isModel) {
Class clz = (Class<?>) f.getType();
// if clz has less fields than the json object, this could be a subclass
Object val = clz.newInstance();
Class<?> recast = recast(val, values.getJSONObject(f.getName()));
if(recast != null) {
val = recast.newInstance();
}
((Model) val).inflate(values.getJSONObject(f.getName()));
f.set(this, val);
} else {
f.set(this, values.get(f.getName()));
}
}
}
}
public static int[] parseJSONAsIntArray(String value) {
String[] intStrings = value.substring(1, value.length() - 1).split(",");
int[] ints = new int[intStrings.length];
for(int f=0; f<intStrings.length; f++) {
ints[f] = Integer.parseInt(intStrings[f]);
}
return ints;
}
public static JSONArray parseIntArrayAsJSON(int[] ints) {
JSONArray intArray = new JSONArray();
for(int f : ints) {
intArray.put(f);
}
return intArray;
}
public static float[] parseJSONAsFloatArray(String value) {
String[] floatStrings = value.substring(1, value.length() - 1).split(",");
float[] floats = new float[floatStrings.length];
for(int f=0; f<floatStrings.length; f++) {
floats[f] = Float.parseFloat(floatStrings[f]);
}
return floats;
}
public static JSONArray parseFloatArrayAsJSON(float[] floats) {
JSONArray floatArray = new JSONArray();
for(float f : floats) {
try {
floatArray.put(f);
} catch (JSONException e) {
Log.e(LOG, e.toString());
e.printStackTrace();
}
}
return floatArray;
}
@SuppressWarnings("rawtypes")
public JSONObject asJson() {
fields = this.getClass().getFields();
JSONObject json = new JSONObject();
for(Field f : fields) {
f.setAccessible(true);
try {
Object value = f.get(this);
if(f.getName().contains("this$")) {
continue;
}
if(f.getName().equals("NULL") || f.getName().equals("LOG")) {
continue;
}
boolean isModel = false;
if(f.getType().getSuperclass() == Model.class) {
isModel = true;
}
if(f.getType() == List.class) {
synchronized (value)
{
JSONArray subValue = new JSONArray();
Iterator it = ((List<?>) value).iterator();
while (it.hasNext()){
Object v = it.next();
if(v instanceof Model) {
subValue.put(((Model) v).asJson());
} else {
subValue.put(v);
}
}
json.put(f.getName(), subValue);
}
} else if(f.getType() == byte[].class) {
json.put(f.getName(), new String((byte[]) value));
} else if(f.getType() == float[].class) {
json.put(f.getName(), parseFloatArrayAsJSON((float[]) value));
} else if(f.getType() == int[].class) {
json.put(f.getName(), parseIntArrayAsJSON((int[]) value));
} else if(isModel) {
json.put(f.getName(), ((Model) value).asJson());
} else {
json.put(f.getName(), value);
}
} catch (IllegalArgumentException e) {
Log.d(LOG, e.toString());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.d(LOG, e.toString());
e.printStackTrace();
} catch (JSONException e) {
Log.d(LOG, e.toString());
e.printStackTrace();
} catch (NullPointerException e) {
}
}
return json;
}
@SuppressWarnings("rawtypes")
public String asCSV() {
fields = this.getClass().getFields();
StringBuffer header = new StringBuffer();
StringBuffer values = new StringBuffer();
for(Field f : fields) {
f.setAccessible(true);
header.append(f.getName()).append(',');
try {
Object value = f.get(this);
if(f.getName().contains("this$")) {
continue;
}
if(f.getName().equals("NULL") || f.getName().equals("LOG")) {
continue;
}
boolean isModel = false;
if(f.getType().getSuperclass() == Model.class) {
isModel = true;
}
if(f.getType() == List.class) {
synchronized (value)
{
Iterator it = ((List<?>) value).iterator();
while (it.hasNext()){
Object v = it.next();
if(v instanceof Model) {
values.append(((Model) v).asCSV()).append(';');
} else {
values.append(v).append(';');
}
}
}
} else if(f.getType() == byte[].class) {
values.append(new String((byte[]) value)).append(',');
} else if(f.getType() == float[].class) {
for (float val : (float[]) value)
{
values.append(val).append(';');
}
values.append(',');
} else if(f.getType() == int[].class) {
for (int val : (int[]) value)
{
values.append(val).append(';');
}
values.append(',');
} else if(isModel) {
values.append(((Model) value).asCSV()).append(',');
} else {
values.append(value).append(',');
}
} catch (IllegalArgumentException e) {
Log.e(LOG, "error making CSV", e);
} catch (IllegalAccessException e) {
Log.e(LOG, "error making CSV", e);
} catch (JSONException e) {
Log.e(LOG, "error making CSV", e);
} catch (NullPointerException e) {
Log.e(LOG, "error making CSV", e);
}
}
return header.toString() + '\n' + values.toString() + '\n';
}
}