package com.litesuits.http.request.query; import com.litesuits.http.data.Consts; import com.litesuits.http.request.param.HttpCustomParam; import com.litesuits.http.request.param.HttpCustomParam.CustomValueBuilder; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; /** * when uri query parameter's value is complex, build value into default style. * in this case, value will intelligently translate to default string. * <p/> * such as : * http://def.so? mapkey={k=v,k1=v1} & arraykey={v1,v2,v3} & * fieldName={field1Name=value1, field2Name=value2} * <p/> * rule is : * Map : map={k=v,k1=v1} * Array : k={v1,v2,v3} * JavaObject(model) : fieldName={field1Name=value1, field2Name=value2} * * @author MaTianyu * 2014-1-4下午5:06:37 */ public class SimpleQueryBuilder extends ModelQueryBuilder { @Override protected CharSequence buildSencondaryValue(Object model) { try { StringBuilder sb = new StringBuilder(); if (model instanceof Collection<?> || model instanceof Object[]) { // when value is array ,use '[' and ']' to enclose data, use ',' to // split array value. Object[] objs = model instanceof Collection<?> ? ((Collection<?>) model).toArray() : (Object[]) model; buildUriKey(sb, null).append(Consts.ARRAY_ECLOSING_LEFT); int i = 0, size = objs.length; for (Object v : objs) { buildMoreLevelValue(sb, null, v, ++i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); } sb.append(Consts.ARRAY_ECLOSING_RIGHT); } else if (model instanceof Map<?, ?>) { Map<?, ?> map = (Map<?, ?>) model; // when value is map ,use '{' and '}' to enclose data, use ',' to // split array value. buildUriKey(sb, null).append(Consts.KV_ECLOSING_LEFT); int i = 0, size = map.size(); for (Entry<?, ?> v : map.entrySet()) { if (v.getKey() instanceof CharSequence || v.getKey() instanceof Character) { buildMoreLevelValue(sb, v.getKey().toString(), v.getValue(), ++i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); } else { buildMoreLevelValue(sb, v.getKey().getClass().getSimpleName(), v.getValue(), ++i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); } } sb.append(Consts.KV_ECLOSING_RIGHT); } else { // find all field. ArrayList<Field> fieldList = getAllDeclaredFields(model.getClass()); // build string for (int i = 0, size = fieldList.size() - 1; i <= size; i++) { Field f = fieldList.get(i); f.setAccessible(true); String key = f.getName(); Object value = f.get(model); if (value != null) { // value is primitive sb.append(Consts.KV_ECLOSING_LEFT); buildMoreLevelValue(sb, key, value, i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); sb.append(Consts.KV_ECLOSING_RIGHT); } } } return sb; } catch (Exception e) { e.printStackTrace(); } return null; } private void buildMoreLevelValue(StringBuilder sb, String key, Object value, String split) throws UnsupportedEncodingException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { // when value is null, just return. if (value == null) { return; } if (value instanceof Number || value instanceof CharSequence || value instanceof Character) { // when value is primitive , build as "key=value" buildUriKey(sb, key).append(encode(value.toString())).append(split); } else if (value instanceof HttpCustomParam) { // when value is inherited from Request.Builder , build as // "key="+method.invoke(). Method methods[] = HttpCustomParam.class.getDeclaredMethods(); for (Method m : methods) { // invoke the method which has specified Annotation if (m.getAnnotation(CustomValueBuilder.class) != null) { m.setAccessible(true); Object v = m.invoke(value); if (v != null) { buildUriKey(sb, key).append(encode(v.toString())).append(split); } break; } } } else if (value instanceof Collection<?> || value instanceof Object[]) { // when value is array ,use '[' and ']' to enclose data, use ',' to // split array value. Object[] objs = value instanceof Collection<?> ? ((Collection<?>) value).toArray() : (Object[]) value; buildUriKey(sb, key).append(Consts.ARRAY_ECLOSING_LEFT); int i = 0, size = objs.length; for (Object v : objs) { buildMoreLevelValue(sb, null, v, ++i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); } sb.append(Consts.ARRAY_ECLOSING_RIGHT).append(split); } else if (value instanceof Map<?, ?>) { Map<?, ?> map = (Map<?, ?>) value; // when value is map ,use '{' and '}' to enclose data, use ',' to // split array value. buildUriKey(sb, key).append(Consts.KV_ECLOSING_LEFT); int i = 0, size = map.size(); for (Entry<?, ?> v : map.entrySet()) { if (v.getKey() instanceof CharSequence || v.getKey() instanceof Character) { buildMoreLevelValue(sb, v.getKey().toString(), v.getValue(), ++i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); } else { buildMoreLevelValue(sb, v.getKey().getClass().getSimpleName(), v.getValue(), ++i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); } } sb.append(Consts.KV_ECLOSING_RIGHT).append(split); } else { buildUriKey(sb, key); sb.append(Consts.KV_ECLOSING_LEFT); // find all field. ArrayList<Field> fieldList = getAllDeclaredFields(value.getClass()); for (int i = 0, size = fieldList.size() - 1; i <= size; i++) { Field f = fieldList.get(i); f.setAccessible(true); String nextKey = f.getName(); Object nextValue = f.get(value); if (nextValue != null) { sb.append(Consts.KV_ECLOSING_LEFT); buildMoreLevelValue(sb, nextKey, nextValue, i == size ? Consts.NONE_SPLIT : Consts.SECOND_LEVEL_SPLIT); sb.append(Consts.KV_ECLOSING_RIGHT); } } sb.append(Consts.KV_ECLOSING_RIGHT).append(split); } } }