package com.senseidb.util; import com.senseidb.search.query.BooleanQueryConstructor; import com.senseidb.search.query.QueryConstructor; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searchable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Utility class for constructing custom objects using object constructors * or static builder methods within the class. Primarily used in construction * of custom query and custom collector objects. * @author darya */ public class ObjectContructorUtil { public static final String CONSTRUCTOR = "constructor"; public static final String STATIC_METHOD = "static_method"; public static final String USE_DEFAULT_SEARCHABLE = "use_default_searchable"; public static final String USE_CONSTRUCTED_QUERY = "use_constructed_query"; public static final String METHOD_NAME = "method_name"; public static final String NUM_ARGS = "num_args"; public static final String ARGS = "args"; public static final String TYPE = "type"; public static final String VALUE = "value"; public static final String TYPE_INT = "int"; public static final String TYPE_BOOLEAN = "boolean"; public static final String TYPE_FLOAT = "float"; public static final String TYPE_DOUBLE = "double"; public static final String TYPE_QUERY = "query"; public static final String TYPE_MAP = "map"; public static final String TYPE_LIST = "list"; public static final String TYPE_STRING = "string"; public static final String TYPE_ARRAY = "array"; public static final String TYPE_CLASS = "class"; public static final String TYPE_ENUM = "enum"; public static final String VALUE_NULL = "null"; public static final String SEPERATER = ":"; public static final String COMMA = ","; public static final String ARRAY_VALUE_SEPERATOR = "^V"; public static Object constructObject(String className, JSONObject jsonQuery, QueryParser queryParser, Query q, Searchable searchable) throws JSONException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { JSONObject classConstructor = jsonQuery.optJSONObject(CONSTRUCTOR); JSONObject methodCall = jsonQuery.optJSONObject(STATIC_METHOD); int numArgs = 0; boolean isContructor = classConstructor == null ? false : true; boolean isMethod = methodCall == null ? false : true; JSONObject argumentsJson = null; if (isContructor) { numArgs = classConstructor.optInt(NUM_ARGS); argumentsJson = classConstructor.optJSONObject(ARGS); } else if (isMethod) { numArgs = methodCall.optInt(NUM_ARGS); argumentsJson = methodCall.optJSONObject(ARGS); } if (isContructor && numArgs == 0) { return Class.forName(className).newInstance(); } Iterator argsIterator = argumentsJson.keys(); Map<Integer, Object> arguments = new HashMap<Integer, Object>(); Map<Integer, Class> argumentsType = new HashMap<Integer, Class>(); while (argsIterator.hasNext()) { Integer argIndex = Integer.parseInt((String) argsIterator.next()); JSONObject argInfo = (JSONObject) argumentsJson.get(argIndex.toString()); String objectType = (String) argInfo.get(TYPE); String objectValue = null; if (objectType.equals(TYPE_BOOLEAN)) { objectValue = (String) argInfo.get(VALUE); arguments.put(argIndex, Boolean.parseBoolean(objectValue)); argumentsType.put(argIndex, boolean.class); } else if (objectType.equals(TYPE_INT)) { objectValue = (String) argInfo.get(VALUE); arguments.put(argIndex, Integer.parseInt(objectValue)); argumentsType.put(argIndex, int.class); } else if (objectType.equals(TYPE_FLOAT)) { objectValue = (String) argInfo.get(VALUE); arguments.put(argIndex, Float.parseFloat(objectValue)); argumentsType.put(argIndex, float.class); } else if (objectType.equals(TYPE_DOUBLE)) { objectValue = (String) argInfo.get(VALUE); arguments.put(argIndex, Double.parseDouble(objectValue)); argumentsType.put(argIndex, double.class); } else if (objectType.equals(TYPE_STRING)) { objectValue = (String) argInfo.get(VALUE); if (!objectValue.equals(VALUE_NULL)) { arguments.put(argIndex, objectValue); } else { arguments.put(argIndex, null); } argumentsType.put(argIndex, String.class); } else if (objectType.equals(TYPE_MAP)) { Map<Object, Object> objectMap = new HashMap<Object, Object>(); if(argInfo.getString(VALUE).equals(VALUE_NULL)) { arguments.put(argIndex, null); argumentsType.put(argIndex, Map.class); continue; } JSONObject jsonMap = argInfo.getJSONObject(VALUE); Iterator mapIter = jsonMap.keys(); while(mapIter.hasNext()) { String jKey = (String) mapIter.next(); objectMap.put(jKey, jsonMap.get(jKey)); } arguments.put(argIndex, objectMap); argumentsType.put(argIndex, Map.class); } else if (objectType.equals(TYPE_LIST)) { List<String> list = new ArrayList<String>(); JSONArray jsonArray = argInfo.getJSONArray(VALUE); for (int i = 0; i < jsonArray.length(); i++) { list.add(jsonArray.getString(i)); } arguments.put(argIndex, list); argumentsType.put(argIndex, List.class); } else if (objectType.contains(TYPE_ARRAY + SEPERATER)) { objectValue = (String) argInfo.get(VALUE); String[] split = objectType.split(SEPERATER); String[] cVals = objectValue.split("\\" + ARRAY_VALUE_SEPERATOR); String cName = split[1]; Integer count = Integer.parseInt(split[3]); Class objectClass = Class.forName(cName); Class arrayClass = Array.newInstance(objectClass, count).getClass(); Object o = Array.newInstance(objectClass, count); for (int i = 0; i < count; i++) { JSONObject v = new JSONObject(cVals[i]); Object val = constructObject(cName, v, queryParser, q, searchable); Array.set(o, i, val); } Object[] oo = (Object[])o; arguments.put(argIndex, oo); argumentsType.put(argIndex, arrayClass); } else if (objectType.equals(TYPE_QUERY)) { objectValue = (String) argInfo.get(VALUE); JSONObject qTmp = new JSONObject(objectValue); qTmp = qTmp.optJSONObject(TYPE_QUERY).optJSONObject(BooleanQueryConstructor.QUERY_TYPE); QueryConstructor queryConstructor = QueryConstructor.getQueryConstructor(BooleanQueryConstructor.QUERY_TYPE, queryParser); Query baseQuery = queryConstructor.doConstructQuery(qTmp); arguments.put(argIndex, baseQuery); argumentsType.put(argIndex, Query.class); } else if (objectType.contains(TYPE_CLASS + SEPERATER)) { objectValue = (String) argInfo.get(VALUE); String[] split = objectType.split(SEPERATER); String cName = split[1]; Class objectClass = Class.forName(cName); boolean useSuperClass = Boolean.parseBoolean(split[2]); if (useSuperClass) { Class c = objectClass; List<Class> classList = new ArrayList<Class>(); while(c != null) { classList.add(c); c = c.getSuperclass(); } objectClass = classList.get(classList.size() - 2); } if (objectValue.equals(VALUE_NULL)) { arguments.put(argIndex, null); argumentsType.put(argIndex, objectClass); continue; } else if (objectValue.equals(USE_DEFAULT_SEARCHABLE)) { arguments.put(argIndex, searchable); argumentsType.put(argIndex, Searchable.class); continue; } else if (objectValue.equals(USE_CONSTRUCTED_QUERY)) { arguments.put(argIndex, q); argumentsType.put(argIndex, Query.class); continue; } Object cObj = constructObject(cName, new JSONObject(objectValue), queryParser, q, searchable); arguments.put(argIndex, cObj); argumentsType.put(argIndex, objectClass); } else if (objectType.contains(TYPE_ENUM + SEPERATER)) { objectValue = (String) argInfo.get(VALUE); String[] split = objectType.split(SEPERATER); String cName = split[1]; boolean useSuperClass = Boolean.parseBoolean(split[2]); Class objectClass = Class.forName(cName); if (useSuperClass) { Class c = objectClass; List<Class> classList = new ArrayList<Class>(); while(c != null) { classList.add(c); c = c.getSuperclass(); } objectClass = classList.get(classList.size() - 2); } if (objectClass.isEnum()) { Object[] enumConstants = objectClass.getEnumConstants(); for (Object o : enumConstants) { if (objectValue.equals(o.toString())) { arguments.put(argIndex, o); argumentsType.put(argIndex, objectClass); continue; } } } } } Class [] argClasses = new Class[argumentsType.size()]; Object [] argValues = new Object[arguments.size()]; for (Map.Entry<Integer, Class> entry : argumentsType.entrySet()) { argClasses[entry.getKey() - 1] = entry.getValue(); } for (Map.Entry<Integer, Object> entry : arguments.entrySet()) { argValues[entry.getKey() - 1] = entry.getValue(); } Class x = Class.forName(className); if (isContructor) { Constructor xConstructor = x.getConstructor(argClasses); Object obj = xConstructor.newInstance(argValues); return obj; } else if (isMethod) { String methodName = methodCall.optString(METHOD_NAME); Method staticMethod = x.getMethod(methodName, argClasses); Object obj = staticMethod.invoke(null, argValues); return obj; } return null; } }