+++ /dev/null
-/*--------------------------------------------------------------------------\r
- * Copyright 2007 Taro L. Saito\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- *--------------------------------------------------------------------------*/\r
-//--------------------------------------\r
-// XerialJ Project\r
-//\r
-// BeanUtil.java\r
-// Since: 2007/03/29\r
-//\r
-// $URL$ \r
-// $Author$\r
-//--------------------------------------\r
-package org.xerial.util.bean;\r
-\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.io.Reader;\r
-import java.io.StringReader;\r
-import java.lang.reflect.InvocationTargetException;\r
-import java.lang.reflect.Method;\r
-import java.lang.reflect.Modifier;\r
-import java.lang.reflect.ParameterizedType;\r
-import java.lang.reflect.Type;\r
-import java.net.URL;\r
-import java.sql.ResultSet;\r
-import java.sql.ResultSetMetaData;\r
-import java.sql.SQLException;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.Vector;\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-\r
-import org.w3c.dom.Element;\r
-import org.xerial.core.XerialErrorCode;\r
-import org.xerial.core.XerialException;\r
-import org.xerial.json.JSONArray;\r
-import org.xerial.json.JSONBoolean;\r
-import org.xerial.json.JSONDouble;\r
-import org.xerial.json.JSONInteger;\r
-import org.xerial.json.JSONLong;\r
-import org.xerial.json.JSONObject;\r
-import org.xerial.json.JSONString;\r
-import org.xerial.json.JSONValue;\r
-import org.xerial.lens.Lens;\r
-import org.xerial.silk.SilkWalker;\r
-import org.xerial.util.Pair;\r
-import org.xerial.util.bean.impl.Appender;\r
-import org.xerial.util.bean.impl.ArraySetter;\r
-import org.xerial.util.bean.impl.BeanBindingProcess;\r
-import org.xerial.util.bean.impl.BeanStreamReader;\r
-import org.xerial.util.bean.impl.BeanUtilImpl;\r
-import org.xerial.util.bean.impl.BindRuleGeneratorForBeanStream;\r
-import org.xerial.util.bean.impl.CollectionAdder;\r
-import org.xerial.util.bean.impl.CollectionSetter;\r
-import org.xerial.util.bean.impl.Getter;\r
-import org.xerial.util.bean.impl.MapPutter;\r
-import org.xerial.util.bean.impl.MapSetter;\r
-import org.xerial.util.bean.impl.Setter;\r
-import org.xerial.util.xml.XMLGenerator;\r
-\r
-/**\r
- * BeanUtil class supports data binding between JSON data and a bean class.\r
- * \r
- * A bean class must have the followings: - A public default (no argument)\r
- * constructor - To bind data object to a class, there must be public\r
- * getter/seter methods in the target class, prefixd with get/set. -- Allowable\r
- * data classes are: ---- primitive types: int/Integer, double/Double,\r
- * float/Float, boolean/Boolean, String ---- bean classes ---- Collection of\r
- * allowable data classes. e.g. List<String>, Set<Integer>, etc. ---- Map with\r
- * key and value classes. key and value also must be allowable classes. ------\r
- * to bind JSON data to map object, putSomething() method is requried. See the\r
- * following description.\r
- * \r
- * For example, a Person class with integer id and string name values must have\r
- * <code>public int getId()</code> and <code>public String getName()</code>, and\r
- * <code>public void setId(int id)</code> and\r
- * <code>public void setName(String name)</code> methods.\r
- * \r
- * Usage Example. <code>\r
- * class Person {\r
- * int id;\r
- * String name;\r
- * public Person();\r
- * public Person(int id, String name)\r
- * { this.id = id; this.name = name; } \r
- * \r
- * public int getId() { return id; }\r
- * public String getName() { return name; }\r
- * public void setId(int id) { this.id = id; }\r
- * public void setName(String name) { this.name = name; }\r
- * }\r
- * \r
- * Person p1 = new Person(1, "leo");\r
- * String json = BeanUtil.toJSON(p1); // it will give a json string { "id" : 1, "name" : "leo"}\r
- * \r
- * Person p2 = new Person() // empty data\r
- * BeanUtil.populate(p2, json); // fill p2 with a give json data\r
- * // p2.id = 1, p2.name = "leo"\r
- * \r
- * </code>\r
- * \r
- * \r
- * \r
- * In the GWT (Google Web Toolkit) of the current version (1.3.3, Mar 2007), the\r
- * generics feature of Jave (since JDK1.5) cannot be used within the client Java\r
- * codes. This is a problematic since GWT cannot compile the following method:\r
- * <code>\r
- * void setPersonList(List<Person> personList) { this.personList = personList; }\r
- * </code> So you have to change the argument of the avobe method as: <code>\r
- * void setPersonList(List personList) { ... }\r
- * </code>\r
- * But, with this setter method, we have no information about the element type\r
- * contained in the List class. So given a JSON data, e.g.,{ "personList" : [\r
- * {"id":1, "name":"leo"}, {"id":2, "name":"taro"}] }, BeanUtil class cannot\r
- * instantiate any Person classes.\r
- * \r
- * To resolve this problem, BeanUtil supports data binding via adder methods.\r
- * For example, instead of using setters, by using the follwing adder, you can\r
- * load several Person class data as a collection:\r
- * <code>void addPersonList(Person person) { personList.add(person); }</code>\r
- * \r
- * Note that, in order to use adders correctly, the target of an adder, that is,\r
- * a collection class, must be initialized before the invocation of the adder.\r
- * For example, <code> \r
- * public PersonList {\r
- * Vector<Person> personList = new Vector<Person>(); // it must be initialized. \r
- * public PersonList() {}\r
- * void addPersonList(Person p) { personList.add(p); }\r
- * public getPersonList() { return personList; } \r
- * }\r
- * </code>\r
- * \r
- * Otherwise, BeanUtil.populate method cuases NullPointerException or some other\r
- * undisired effects.\r
- * \r
- * In BeanUtil, a setter method, whose argument has no generic type parameter,\r
- * e.g., <code>public void setList(List l)</code> never be used to bind JSON\r
- * data to a class instance.\r
- * \r
- * \r
- * In order to use a Bean class with Map<K, V> objects, you must define\r
- * <code>public Map getSomething()</code> and\r
- * <code>public void putSomething(KeyType key, ValueType value)</code> methods,\r
- * since BeanUtil learns class types K, V from the putSomething(K key, V value)\r
- * methods. If a method <code>public void setSomething(Map map)</code> exists,\r
- * BeanUtil simply ignores it.\r
- * \r
- * \r
- * The following is an example of a bean class with Map object. <code>\r
- * public MapBean\r
- * {\r
- * Map entry = new TreeMap();\r
- * \r
- * public MapBean(){}\r
- * \r
- * public void putEntry(Key key, Value v)\r
- * {\r
- * entry.put(key, value);\r
- * }\r
- * \r
- * public Map getEntry()\r
- * {\r
- * return entry;\r
- * }\r
- * }\r
- * </code>\r
- * \r
- * @author leo\r
- * \r
- */\r
-public class BeanUtil\r
-{\r
-\r
- private static HashMap<Class< ? >, BinderSet> _beanOutputRuleRegistry = new HashMap<Class< ? >, BinderSet>();\r
-\r
- private static HashMap<Class< ? >, BinderSet> _beanLoadRuleRegistry = new HashMap<Class< ? >, BinderSet>();\r
-\r
- public static BeanBinderSet getBeanOutputRule(Class< ? > c) throws XerialException {\r
- if (_beanOutputRuleRegistry.containsKey(c))\r
- return _beanOutputRuleRegistry.get(c);\r
- else {\r
- BinderSet beanOutputRule = inspectGetters(c);\r
- _beanOutputRuleRegistry.put(c, beanOutputRule);\r
- return beanOutputRule;\r
- }\r
- }\r
-\r
- public static BeanBinderSet getBeanLoadRule(Class< ? > c) throws XerialException {\r
- if (_beanLoadRuleRegistry.containsKey(c))\r
- return _beanLoadRuleRegistry.get(c);\r
- else {\r
- BinderSet beanLoadRule = inspectSetter(c);\r
- _beanLoadRuleRegistry.put(c, beanLoadRule);\r
- return beanLoadRule;\r
- }\r
- }\r
-\r
- /**\r
- * retrieve getter methods fo the form: getSomething(), then define how to\r
- * use these methods according to the return type of the getter.\r
- * \r
- * If the return type is: - primitiveType type: crate a rule that simply\r
- * invokes getSomething() method, and recieves a value. - collection iterate\r
- * for each element in the collection\r
- * \r
- * @param bean\r
- * @throws UTGBException\r
- */\r
- private static BinderSet inspectGetters(Class< ? > beanClass) throws XerialException {\r
- BinderSet outputRuleSet = new BinderSet(beanClass);\r
-\r
- Method[] method = beanClass.getMethods();\r
- for (Method m : method) {\r
- if (!Modifier.isPublic(m.getModifiers())) // is public?\r
- continue;\r
- String methodName = m.getName();\r
- String parameterName = BeanUtil.pickPropertyName(methodName);\r
- if (parameterName == null || parameterName.length() == 0)\r
- continue;\r
-\r
- if (!methodName.startsWith("get") || methodName.equals("getClass"))\r
- continue; // Object\r
-\r
- Class< ? >[] parameterType = m.getParameterTypes();\r
- if (parameterType.length != 0) // we cannot use the getter unless\r
- // it requires no argument.\r
- continue;\r
- outputRuleSet.addRule(new Getter(m, parameterName));\r
- }\r
-\r
- return outputRuleSet;\r
- }\r
-\r
- public static BinderSet inspectSetter(Class< ? > beanClass) throws XerialException {\r
- BinderSet inputRuleSet = new BinderSet(beanClass);\r
-\r
- // create a rule set of binding rules from setters of the bean class\r
- for (Method method : beanClass.getMethods()) {\r
- String methodName = method.getName();\r
-\r
- String parameterName = BeanUtil.pickPropertyName(methodName); // retrieve\r
- // setter,\r
- // getter,\r
- // adder,\r
- // putter\r
- // parameter\r
- // name\r
- if (parameterName == null)\r
- continue;\r
-\r
- if (!Modifier.isPublic(method.getModifiers())) // is public\r
- // setter/adder/putter?\r
- continue;\r
-\r
- Class< ? >[] parameterType = method.getParameterTypes();\r
- if (methodName.startsWith("put")) {\r
- if (parameterType.length != 2)\r
- continue;\r
-\r
- Class< ? >[] mapElementType = resolveActualTypeOfMapElement(beanClass, parameterType);\r
-\r
- if (parameterName.length() == 0 && TypeInfo.isMap(beanClass)) {\r
- // bean.put(Key k, Value v)\r
- inputRuleSet.addRule(new MapPutter(method, "elem", mapElementType[0], mapElementType[1]));\r
- }\r
- else {\r
- // putSomething(Key k, Value v)\r
- inputRuleSet.addRule(new MapPutter(method, parameterName, mapElementType[0], mapElementType[1]));\r
- }\r
- continue;\r
- }\r
-\r
- if (methodName.startsWith("add")) {\r
- if (parameterType.length != 1)\r
- continue;\r
-\r
- Class< ? > addType = resolveActualTypeOfCollectionElement(beanClass, parameterType[0]);\r
-\r
- if (parameterName.length() == 0 && TypeInfo.isCollection(beanClass)) {\r
- // bean.add(E element)\r
- inputRuleSet.addRule(new CollectionAdder(method, "elem", addType));\r
- }\r
- else {\r
- // bean.addSomething(E element)\r
- inputRuleSet.addRule(new CollectionAdder(method, parameterName, addType));\r
- }\r
- continue;\r
- }\r
-\r
- if (methodName.startsWith("append")) {\r
- if (parameterType.length != 1)\r
- continue;\r
-\r
- Class< ? > addType = resolveActualTypeOfCollectionElement(beanClass, parameterType[0]);\r
- inputRuleSet.addRule(new Appender(addType, method, parameterName));\r
- continue;\r
- }\r
-\r
- if (methodName.startsWith("set")) {\r
- if (parameterType.length != 1 || parameterName.length() == 0)\r
- continue;\r
-\r
- Class< ? > inputTypeOfTheSetter = parameterType[0];\r
-\r
- if (inputTypeOfTheSetter.isArray()) {\r
- // setSomething(int[] array) etc\r
- Class< ? > componentType = inputTypeOfTheSetter.getComponentType();\r
- inputRuleSet.addRule(new ArraySetter(method, parameterName, componentType));\r
- }\r
- else if (TypeInfo.isCollection(inputTypeOfTheSetter)) {\r
- if (method.getGenericParameterTypes()[0] instanceof ParameterizedType) {\r
- ParameterizedType genericSetterArgumentType = (ParameterizedType) method\r
- .getGenericParameterTypes()[0];\r
- Type[] actualTypeList = genericSetterArgumentType.getActualTypeArguments();\r
- if (actualTypeList.length > 0) {\r
- Class< ? > elementType = resolveRawType(actualTypeList[0]);\r
- inputRuleSet.addRule(new CollectionSetter(method, parameterName, inputTypeOfTheSetter,\r
- elementType));\r
- }\r
- }\r
- // setSomething(Collection) method wihtout any type\r
- // parameter cannot be used to bind JSON data, thus skip\r
- }\r
- else if (TypeInfo.isMap(inputTypeOfTheSetter)) {\r
- // setSomething(Map<K, V> map) method\r
- Pair<Class< ? >, Class< ? >> keyValueTypePair = getGenericMapTypesOfMethodArgument(method, 0);\r
- if (keyValueTypePair != null) {\r
- inputRuleSet.addRule(new MapSetter(method, parameterName, inputTypeOfTheSetter,\r
- keyValueTypePair.getFirst(), keyValueTypePair.getSecond()));\r
- continue;\r
- }\r
- // setMap(Map map) without any type parameter cannot be used\r
- }\r
- else {\r
- inputRuleSet.addRule(new Setter(method, parameterName, inputTypeOfTheSetter));\r
- }\r
- }\r
- }\r
-\r
- return inputRuleSet;\r
- }\r
-\r
- public static Pair<Class< ? >, Class< ? >> getGenericMapTypesOfMethodArgument(Method method, int argIndex) {\r
-\r
- ParameterizedType genericSetterArgumentType = getParentParameterizedType(\r
- method.getGenericParameterTypes()[argIndex], Map.class);\r
-\r
- if (genericSetterArgumentType != null) {\r
- Type[] actualTypeList = genericSetterArgumentType.getActualTypeArguments();\r
- if (actualTypeList.length >= 2) {\r
- Class< ? > keyType = resolveRawType(actualTypeList[0]);\r
- Class< ? > valueType = resolveRawType(actualTypeList[1]);\r
- return new Pair<Class< ? >, Class< ? >>(keyType, valueType);\r
- }\r
- }\r
-\r
- return null;\r
-\r
- }\r
-\r
- public static ParameterizedType getParameterizedType(Type t) {\r
- if (t == null)\r
- return null;\r
-\r
- if (t instanceof ParameterizedType) {\r
- ParameterizedType pt = (ParameterizedType) t;\r
- return pt;\r
- }\r
- if (t instanceof Class)\r
- return getParameterizedType(((Class< ? >) t).getGenericSuperclass());\r
- else\r
- return null;\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public static ParameterizedType getParentParameterizedType(Type t, Class target) {\r
- if (t == null)\r
- return null;\r
-\r
- if (t instanceof ParameterizedType) {\r
- ParameterizedType pt = (ParameterizedType) t;\r
- if (target.isAssignableFrom((Class) pt.getRawType())) {\r
- return pt;\r
- }\r
- }\r
-\r
- if (t instanceof Class) {\r
- Class c = (Class) t;\r
- return getParentParameterizedType(c.getGenericSuperclass(), target);\r
- }\r
- else\r
- return null;\r
- }\r
-\r
- public static Class< ? > resolveActualTypeOfCollectionElement(Type type, Class< ? > orig) {\r
- ParameterizedType pt = getParentParameterizedType(type, Collection.class);\r
- if (pt != null) {\r
- Type[] actualType = pt.getActualTypeArguments();\r
- if (actualType.length > 0)\r
- return resolveRawType(actualType[0], orig);\r
- }\r
- return orig;\r
- }\r
-\r
- public static Class< ? >[] resolveActualTypeOfMapElement(Type type, Class< ? >[] orig) {\r
- ParameterizedType pt = getParentParameterizedType(type, Map.class);\r
- if (pt != null) {\r
- Type[] actualType = pt.getActualTypeArguments();\r
- if (actualType.length > 0)\r
- return new Class[] { resolveRawType(actualType[0], orig[0]), resolveRawType(actualType[1], orig[1]) };\r
- }\r
- return orig;\r
- }\r
-\r
- private static Class< ? > resolveRawType(Type type, Class< ? > orig) {\r
- if (type instanceof ParameterizedType) {\r
- ParameterizedType pt = (ParameterizedType) type;\r
- return resolveRawType(pt.getRawType(), orig);\r
- }\r
- else if (type instanceof Class)\r
- return (Class< ? >) type;\r
- else\r
- return orig;\r
- }\r
-\r
- private static Class< ? > resolveRawType(Type type) {\r
- if (type instanceof ParameterizedType) {\r
- ParameterizedType pt = (ParameterizedType) type;\r
- return resolveRawType(pt.getRawType());\r
- }\r
- else if (type instanceof Class)\r
- return (Class< ? >) type;\r
- else\r
- return Object.class;\r
- }\r
-\r
- // non constructable\r
- private BeanUtil() {}\r
-\r
- /**\r
- * @param methodName\r
- * method name of getter or setters. e.g. setSomething,\r
- * getSomething\r
- * @param patternType\r
- * set(ter) or get(ter)\r
- * @return property name of the method name\r
- * \r
- * \r
- * \r
- */\r
- public static String pickPropertyName(String methodName) {\r
- Matcher m = null;\r
- m = _setGetAddMethodPattern.matcher(methodName);\r
- if (!m.matches())\r
- return null;\r
- else {\r
- if (m.group(2) != null)\r
- return m.group(3).toLowerCase() + m.group(4);\r
- else\r
- return "";\r
- }\r
- }\r
-\r
- static private Pattern _setGetAddMethodPattern = Pattern.compile("^(set|get|add|put|append)((\\S)(\\S*))?");\r
-\r
- private static class BeanToXMLProcess\r
- {\r
- private ByteArrayOutputStream _buffer = new ByteArrayOutputStream();\r
-\r
- private XMLGenerator _out = new XMLGenerator(_buffer);\r
-\r
- public BeanToXMLProcess() {\r
-\r
- }\r
-\r
- public String generateXML(String tagName, Object bean) throws XerialException {\r
- try {\r
- toXML(tagName, bean);\r
- _out.endDocument();\r
- _out.flush();\r
- return _buffer.toString();\r
- }\r
- catch (IllegalArgumentException e) {\r
- throw new XerialException(XerialErrorCode.IllegalArgument, e);\r
- }\r
- }\r
-\r
- private void toXML(String tagName, Object bean) throws XerialException {\r
- if (bean == null)\r
- return;\r
-\r
- Class< ? > beanClass = bean.getClass();\r
-\r
- if (beanClass.isArray()) {\r
- Object[] array = (Object[]) bean;\r
- int i = 0;\r
- for (; i < array.length - 1; i++) {\r
- toXML(tagName, array[i]);\r
- _out.text(",");\r
- }\r
- toXML(tagName, array[i]);\r
- }\r
- else if (TypeInfo.isBasicType(beanClass)) {\r
- _out.element(tagName, bean.toString());\r
- }\r
- else {\r
- if (TypeInfo.isCollection(beanClass)) {\r
- Collection< ? > collection = (Collection< ? >) bean;\r
- for (Object elem : collection) {\r
- toXML(tagName, elem);\r
- }\r
- }\r
- else if (TypeInfo.isMap(beanClass)) {\r
- Map< ? , ? > map = (Map< ? , ? >) bean;\r
-\r
- for (Object key : map.keySet()) {\r
- _out.startTag(tagName);\r
- _out.element("key", key.toString());\r
- _out.element("value", map.get(key).toString());\r
- _out.endTag();\r
- }\r
- }\r
- else {\r
- // return an XML elemenet\r
- _out.startTag(tagName);\r
- BeanBinderSet outputRuleSet = BeanUtil.getBeanOutputRule(beanClass);\r
- for (BeanBinder rule : outputRuleSet.getBindRules()) {\r
- Method getter = rule.getMethod();\r
- String parameterName = rule.getParameterName();\r
-\r
- Object parameterValue = invokeGetterMethod(getter, bean);\r
- toXML(parameterName, parameterValue);\r
- }\r
- _out.endTag();\r
- }\r
- }\r
-\r
- }\r
- }\r
-\r
- public static String toXML(String tagName, Object bean) throws XerialException {\r
- BeanToXMLProcess bp = new BeanToXMLProcess();\r
- return bp.generateXML(tagName, bean);\r
- }\r
-\r
- public static String toJSONFromResultSet(ResultSet resultSet) throws SQLException {\r
- StringBuilder builder = new StringBuilder();\r
- ResultSetMetaData metadata = resultSet.getMetaData();\r
- int colCount = metadata.getColumnCount();\r
- builder.append("{");\r
- for (int i = 1; i <= colCount; i++) {\r
- if (i >= 2)\r
- builder.append(",");\r
- Object value = resultSet.getObject(i);\r
- builder.append("\"");\r
- builder.append(metadata.getColumnName(i));\r
- builder.append("\":");\r
- if (value != null) {\r
- builder.append("\"");\r
- builder.append(value.toString());\r
- builder.append("\"");\r
- }\r
- else\r
- builder.append("null");\r
- }\r
- builder.append("}");\r
- return builder.toString();\r
- }\r
-\r
- public static String toJSON(Object bean) throws XerialException {\r
- return outputAsJSONValue(bean).toString();\r
- }\r
-\r
- public static JSONObject toJSONObject(Object bean) throws XerialException {\r
- return outputAsJSONValue(bean).getJSONObject();\r
- }\r
-\r
- public static JSONObject toJSONObject(Collection< ? > collection) throws XerialException {\r
- JSONObject jsonObj = new JSONObject();\r
- jsonObj.put("elem", outputAsJSONValue(collection));\r
- return jsonObj;\r
- }\r
-\r
- public static JSONValue getValue(Object bean, String propertyName) throws XerialException {\r
- JSONObject json = toJSONObject(bean);\r
- return json.get(propertyName);\r
- }\r
-\r
- private static JSONValue outputAsJSONValue(Object bean) throws XerialException {\r
- if (bean == null)\r
- return null;\r
- Class< ? > beanClass = bean.getClass();\r
-\r
- if (beanClass.isArray()) {\r
- Object[] array = (Object[]) bean;\r
- JSONArray jsonArray = new JSONArray();\r
- for (Object obj : array)\r
- jsonArray.add(outputAsJSONValue(obj));\r
- return jsonArray;\r
- }\r
- else if (TypeInfo.isBasicType(beanClass)) {\r
- String jsonStr = bean.toString();\r
- JSONValue value = null;\r
- if (beanClass == String.class)\r
- value = new JSONString(jsonStr);\r
- else if (beanClass == int.class || beanClass == Integer.class)\r
- value = new JSONInteger(jsonStr);\r
- else if (beanClass == double.class || beanClass == Double.class)\r
- value = new JSONDouble(jsonStr);\r
- else if (beanClass == float.class || beanClass == Float.class)\r
- value = new JSONDouble(jsonStr);\r
- else if (beanClass == boolean.class || beanClass == Boolean.class)\r
- value = new JSONBoolean((Boolean) bean);\r
- else if (beanClass == long.class || beanClass == Long.class)\r
- value = new JSONLong(jsonStr);\r
- else\r
- throw new XerialException(XerialErrorCode.InvalidBeanClass, beanClass.toString() + " is not basic type");\r
- return value;\r
- }\r
- else {\r
-\r
- if (TypeInfo.isCollection(beanClass)) {\r
- Collection< ? > collection = (Collection< ? >) bean;\r
- JSONArray jsonArray = new JSONArray();\r
- for (Object obj : collection)\r
- jsonArray.add(outputAsJSONValue(obj));\r
-\r
- if (hasGetter(beanClass)) {\r
- // extended Array class\r
- JSONObject json = new JSONObject();\r
- json.put("elem", jsonArray);\r
- return outputBeanParameters(json, bean);\r
- }\r
- else\r
- return jsonArray;\r
- }\r
- else if (TypeInfo.isMap(beanClass)) {\r
- Map< ? , ? > map = (Map< ? , ? >) bean;\r
- JSONArray jsonArray = new JSONArray();\r
- for (Object key : map.keySet()) {\r
- JSONObject pair = new JSONObject();\r
- pair.put("key", outputAsJSONValue(key));\r
- pair.put("value", outputAsJSONValue(map.get(key)));\r
- jsonArray.add(pair);\r
- }\r
-\r
- BeanBinderSet outputRuleSet = BeanUtil.getBeanOutputRule(beanClass);\r
- if (outputRuleSet.getBindRules().size() > 0) {\r
- // extended Map class\r
- JSONObject json = new JSONObject();\r
- json.put("elem", jsonArray);\r
- return outputBeanParameters(json, bean);\r
- }\r
- else\r
- return jsonArray;\r
- }\r
- else\r
- return outputBeanParameters(new JSONObject(), bean);\r
- }\r
-\r
- }\r
-\r
- private static boolean hasGetter(Class< ? > beanClass) throws XerialException {\r
- BeanBinderSet outputRuleSet = BeanUtil.getBeanOutputRule(beanClass);\r
- return outputRuleSet.getBindRules().size() > 0;\r
- }\r
-\r
- private static JSONObject outputBeanParameters(JSONObject json, Object bean) throws XerialException {\r
- BeanBinderSet outputRuleSet = BeanUtil.getBeanOutputRule(bean.getClass());\r
- for (BeanBinder rule : outputRuleSet.getBindRules()) {\r
- Method getter = rule.getMethod();\r
- String parameterName = rule.getParameterName();\r
-\r
- Object parameterValue = invokeGetterMethod(getter, bean);\r
- if (parameterValue != null)\r
- json.put(parameterName, outputAsJSONValue(parameterValue));\r
- }\r
- return json;\r
- }\r
-\r
- private static Object invokeGetterMethod(Method getter, Object bean) throws XerialException {\r
- try {\r
- return getter.invoke(bean);\r
- }\r
- catch (IllegalArgumentException e) {\r
- throw new XerialException(XerialErrorCode.IllegalArgument, e);\r
- }\r
- catch (IllegalAccessException e) {\r
- throw new XerialException(XerialErrorCode.IllegalAccess, e);\r
- }\r
- catch (InvocationTargetException e) {\r
- throw new XerialException(XerialErrorCode.InvocationTargetException, e);\r
- }\r
- }\r
-\r
- public static void populateBeanWithMap(Object bean, Map< ? , ? > map) throws XerialException {\r
- BeanUtilImpl.populateBeanWithMap(bean, map);\r
- }\r
-\r
- public static void populateBeanWithXML(Object bean, Reader xmlReader) throws XerialException {\r
- if (bean == null)\r
- throw new XerialException(XerialErrorCode.BeanObjectIsNull);\r
-\r
- BeanUtilImpl.populateBeanWithXML(bean, xmlReader);\r
-\r
- }\r
-\r
- public static void populateBeanWithXML(Object bean, String xmlData) throws XerialException {\r
- populateBeanWithXML(bean, new StringReader(xmlData));\r
- }\r
-\r
- public static void populateBeanWithXML(Object bean, Element xmlElement) throws XerialException {\r
- if (xmlElement == null)\r
- return; // there is nothing to bind\r
-\r
- BeanUtilImpl.populateBeanWithXML(bean, xmlElement);\r
- }\r
-\r
- // protected static void populateBeanWithXML(Object bean, Object xmlValue)\r
- // throws BeanException\r
- // {\r
- // if (xmlValue == null)\r
- // return;\r
- //\r
- // Class beanClass = bean.getClass();\r
- // if (TypeInformation.isBasicType(beanClass))\r
- // {\r
- // bean = populateBasicTypeWithXML(beanClass, xmlValue);\r
- // }\r
- // else\r
- // {\r
- // if (TypeInformation.isDOMElement(xmlValue.getClass()))\r
- // populateBeanWithXML(bean, (Element) xmlValue);\r
- // else\r
- // {\r
- // throw new BeanException(XerialErrorCode.UnsupportedXMLDataType,\r
- // "unsupported value type: "\r
- // + xmlValue.getClass().toString());\r
- // }\r
- // }\r
- // }\r
-\r
- public static void populateBeanWithJSON(Object bean, Reader jsonReader) throws XerialException, IOException {\r
- BeanUtilImpl.populateBeanWithJSON(bean, jsonReader);\r
- }\r
-\r
- /**\r
- * fill a bean class with a given JSON data\r
- * \r
- * @param bean\r
- * a bean class\r
- * @param jsonData\r
- * a string representation of a JSON data\r
- * @throws IOException\r
- * @throws InvalidJSONDataException\r
- * when the input json data is invalid (cannot interpret as a\r
- * JSON object)\r
- * @throws InvalidXerialException\r
- * when a bean class has invalid structure\r
- */\r
- public static void populateBeanWithJSON(Object bean, String jsonData) throws XerialException {\r
- // parse the input JSON data\r
- try {\r
- populateBeanWithJSON(bean, new StringReader(jsonData));\r
- }\r
- catch (IOException e) {\r
- throw new XerialException(XerialErrorCode.IOError, e);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * fill a bean class with a given JSONObject data\r
- * \r
- * @param bean\r
- * a bean class\r
- * @param json\r
- * a JSONOBject\r
- * @throws InvalidXerialException\r
- * @throws InvalidJSONDataException\r
- * @throws IllegalAccessException\r
- * @throws InstantiationException\r
- */\r
- public static void populateBean(Object bean, JSONObject json) throws XerialException {\r
- try {\r
- BeanUtilImpl.populateBeanWithJSON(bean, new StringReader(json.toJSONString()));\r
- }\r
- catch (IOException e) {\r
- throw new XerialException(XerialErrorCode.IOError, e.getMessage());\r
- }\r
- }\r
-\r
- /*\r
- * Map<K, V> m m.json = { "elem":[{"key":k1, "value":v1}, {"key":k2, "value":v2}, ...] }\r
- * \r
- * Map a = Map<A, Map<B, C>> a.json = \r
- * { "elem" : [{"key":"a1", "value":{"elem" : [{"key":b1, "value":c1}, {"key":b2, "value":c2}, ...]}}]}, \r
- * "\r
- * \r
- * Collection { "elem" : [f1, f2, ..., ] }\r
- * \r
- * Array [f1, f2, ... ]\r
- * \r
- * When a JSONArray comes as an input, it is bound to Object[]\r
- * \r
- * @throws IllegalAccessException\r
- * @throws InstantiationException\r
- * @throws InvalidJSONDataException\r
- */\r
- protected static void populateBean(Object bean, JSONArray jsonArray) throws XerialException {\r
- Class< ? > beanClass = bean.getClass();\r
- if (beanClass.isArray()) {\r
- Object[] array = (Object[]) bean;\r
- Class< ? > componentType = beanClass.getComponentType();\r
- for (int i = 0; i < jsonArray.size(); i++) {\r
- try {\r
- array[i] = componentType.newInstance();\r
- }\r
- catch (InstantiationException e) {\r
- throw new XerialException(XerialErrorCode.InstantiationFailure, e);\r
- }\r
- catch (IllegalAccessException e) {\r
- throw new XerialException(XerialErrorCode.IllegalAccess, e);\r
- }\r
- populateBeanWithJSON(array[i], jsonArray.get(i));\r
- }\r
- }\r
- else\r
- throw new XerialException(XerialErrorCode.InvalidJSONArray,\r
- "to bind json array to a bean, it must be an instance array (e.g. Object[])");\r
-\r
- }\r
-\r
- protected static void populateBeanWithJSON(Object bean, Object jsonValue) throws XerialException {\r
- if (jsonValue.getClass() == JSONObject.class) {\r
- populateBean(bean, (JSONObject) jsonValue);\r
- }\r
- else if (jsonValue.getClass() == JSONArray.class) {\r
- populateBean(bean, (JSONArray) jsonValue);\r
- }\r
- else {\r
- // the object is a JSONValue\r
- Class< ? > beanClass = bean.getClass();\r
- if (TypeInfo.isBasicType(beanClass)) {\r
- String jsonStr = jsonValue.toString();\r
- if (beanClass == String.class)\r
- bean = new String(jsonStr);\r
- else if (beanClass == int.class || beanClass == Integer.class)\r
- bean = new Integer(jsonStr);\r
- else if (beanClass == double.class || beanClass == Double.class)\r
- bean = new Double(jsonStr);\r
- else if (beanClass == float.class || beanClass == Float.class)\r
- bean = new Float(jsonStr);\r
- else if (beanClass == boolean.class || beanClass == Boolean.class)\r
- bean = new Boolean(jsonStr);\r
- else\r
- throw new XerialException(XerialErrorCode.InvalidBeanClass);\r
- }\r
- }\r
- }\r
-\r
- public static <T> T populateBeanWithSilk(T bean, URL silkResourceLocation) throws XerialException, IOException {\r
- try {\r
- Lens.loadSilk(bean, silkResourceLocation);\r
- }\r
- catch (IOException e) {\r
- throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);\r
- }\r
-\r
- return bean;\r
- }\r
-\r
- public static Object createBeanFromJSON(Class< ? > beanType, Reader jsonReader) throws IOException, XerialException {\r
- try {\r
- return BeanUtilImpl.createBeanFromJSON(beanType, jsonReader);\r
- }\r
- catch (XerialException e) {\r
- throw new XerialException(XerialErrorCode.BindFailure, e.getMessage());\r
- }\r
- }\r
-\r
- public static Object createBeanFromJSON(Class< ? > beanType, String json) throws XerialException {\r
- try {\r
- return BeanUtilImpl.createBeanFromJSON(beanType, new StringReader(json));\r
- }\r
- catch (IOException e) {\r
- throw new XerialException(XerialErrorCode.IOError, e.getMessage());\r
- }\r
- }\r
-\r
- public static Object createInstance(Class< ? > c) throws XerialException {\r
- return TypeInfo.createInstance(c);\r
- }\r
-\r
- public static <E> E createXMLBean(Class<E> valueType, Reader xmlReader) throws XerialException, IOException,\r
- XerialException {\r
-\r
- return BeanUtilImpl.createBeanFromXML(valueType, xmlReader);\r
- }\r
-\r
- public static Object createXMLBean(Class< ? > valueType, String xmlData) throws XerialException {\r
- Object bean;\r
- bean = createInstance(valueType);\r
- populateBeanWithXML(bean, xmlData);\r
- return bean;\r
- }\r
-\r
- public static <T> T createSilkBean(Class<T> beanClass, URL silkFileLocation) throws XerialException {\r
- T bean = TypeInfo.createInstance(beanClass);\r
- BeanBindingProcess bindingProcess = BeanBindingProcess.newBinderWithRootContext(bean);\r
-\r
- try {\r
- SilkWalker walker = new SilkWalker(silkFileLocation);\r
- walker.walk(bindingProcess);\r
- }\r
- catch (IOException e) {\r
- throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);\r
- }\r
-\r
- return bean;\r
- }\r
-\r
- public static <T> void loadJSON(Reader jsonReader, Class<T> beanClass, BeanHandler<T> beanHandler)\r
- throws IOException, XerialException {\r
- BeanBindingProcess bindingProcess = new BeanBindingProcess(new BeanStreamReader<T>(beanHandler),\r
- new BindRuleGeneratorForBeanStream<T>(beanClass));\r
-\r
- JSONStreamWalker walker = new JSONStreamWalker(jsonReader);\r
- walker.walk(bindingProcess);\r
-\r
- }\r
-\r
- public static <T> void loadJSON(Reader jsonReader, Class<T> beanClass, String targetNodeName,\r
- BeanHandler<T> beanHandler) throws IOException, XerialException {\r
-\r
- BeanBindingProcess bindingProcess = new BeanBindingProcess(new BeanStreamReader<T>(beanHandler),\r
- new BindRuleGeneratorForBeanStream<T>(beanClass, targetNodeName));\r
-\r
- JSONStreamWalker walker = new JSONStreamWalker(jsonReader);\r
- walker.walk(bindingProcess);\r
-\r
- }\r
-\r
-}\r
-\r
-enum BeanParameterType {\r
- primitiveType,\r
- primitiveTypeCollection,\r
- primitiveTypeArray,\r
- nestedBean,\r
- beanArray,\r
- beanCollection,\r
- unknownTypeCollection,\r
- primitiveTypeAdder,\r
- beanAdder,\r
- primitiveTypeArrayAdder,\r
- beanArrayAdder,\r
- map\r
-}\r
-\r
-/**\r
- * TODO A hash structure may have better performance, especially for findRule()\r
- * method.\r
- * \r
- * @author leo\r
- * \r
- */\r
-class BinderSet implements BeanBinderSet\r
-{\r
- Class< ? > beanClass;\r
-\r
- Vector<BeanBinder> _bindRule = new Vector<BeanBinder>();\r
-\r
- public BinderSet(Class< ? > beanClass) {\r
- this.beanClass = beanClass;\r
- }\r
-\r
- // @see org.utgenome.util.BeanBinderSet#addRule(org.utgenome.util.Binder)\r
- public void addRule(BeanBinder binder) {\r
- _bindRule.add(binder);\r
- }\r
-\r
- // @see org.utgenome.util.BeanBinderSet#getBindRules()\r
- public Vector<BeanBinder> getBindRules() {\r
- return _bindRule;\r
- }\r
-\r
- // @see org.utgenome.util.BeanBinderSet#findRule(java.lang.String)\r
- public BeanBinder findRule(String name) {\r
- for (BeanBinder rule : _bindRule) {\r
- if (rule.getParameterName().equalsIgnoreCase(name))\r
- return rule;\r
- }\r
- return null;\r
- }\r
-\r
- public MapPutter getStandardMapPutter() {\r
- for (BeanBinder rule : _bindRule) {\r
- if (rule.getMethod().getName().equals("put"))\r
- return MapPutter.class.cast(rule);\r
- }\r
- return null;\r
- }\r
-}\r