com.feilong.core.net.ParamUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.core.net.ParamUtil.java

Source

/*
 * Copyright (C) 2008 feilong
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.feilong.core.net;

import static org.apache.commons.lang3.ArrayUtils.EMPTY_STRING_ARRAY;
import static org.apache.commons.lang3.StringUtils.EMPTY;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.feilong.core.CharsetType;
import com.feilong.core.bean.ConvertUtil;
import com.feilong.core.lang.StringUtil;
import com.feilong.core.util.MapUtil;
import com.feilong.core.util.SortUtil;

import static com.feilong.core.URIComponents.AMPERSAND;
import static com.feilong.core.URIComponents.QUESTIONMARK;
import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.bean.ConvertUtil.toMap;
import static com.feilong.core.util.MapUtil.newLinkedHashMap;
import static com.feilong.core.util.SortUtil.sortByKeyAsc;

/**
 * ??.
 *
 * @author <a href="http://feitianbenyue.iteye.com/">feilong</a>
 * @see "org.springframework.web.util.UriComponentsBuilder"
 * @see "org.apache.http.client.utils.URIBuilder"
 * @since 1.0.0
 */
public final class ParamUtil {

    /** The Constant log. */
    private static final Logger LOGGER = LoggerFactory.getLogger(ParamUtil.class);

    /** Don't let anyone instantiate this class. */
    private ParamUtil() {
        //AssertionError?. ?????. ???.
        //see Effective Java 2nd
        throw new AssertionError("No " + getClass().getName() + " instances for you!");
    }

    // ************************************addParameter******************************************************

    /**
     * ?,uri????,?.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String uri = "http://www.feilong.com:8888/esprit-frontend/search.htm?{@code keyword=%E6%81%A4&page=}";
     * String pageParamName = "label";
     * String prePageNo = "2-5-8-12";
     * LOGGER.info(ParamUtil.addParameter(uri, pageParamName, prePageNo, UTF8));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code http://www.feilong.com:8888/esprit-frontend/search.htm?keyword=%E6%81%A4&page=&label=2-5-8-12}
     * </pre>
     * 
     * </blockquote>
     *
     * @param uriString
     *            ? ?,?,??,<br>
     *            ??, ?
     * @param paramName
     *            ???
     * @param parameValue
     *            ?,? {@link String} 
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return ?,uri????,?<br>
     *          <code>uriString</code> nullempty, {@link StringUtils#EMPTY}<br>
     * @see #addParameterSingleValueMap(String, Map, String)
     */
    public static String addParameter(String uriString, String paramName, Object parameValue, String charsetType) {
        return addParameterSingleValueMap(uriString, toMap(paramName, "" + parameValue), charsetType);
    }

    /**
     * ?,uri????,?.
     * 
     * <h3>1:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String beforeUrl = "www.baidu.com";
     * Map{@code <String, String>} keyAndArrayMap = new LinkedHashMap{@code <String, String>}();
     * 
     * keyAndArrayMap.put("province", "??");
     * keyAndArrayMap.put("city", "?");
     * 
     * LOGGER.info(ParamUtil.addParameterSingleValueMap(beforeUrl, keyAndArrayMap, UTF8));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code www.baidu.com?province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
     * </pre>
     * 
     * </blockquote>
     * <h3>2:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String beforeUrl = "www.baidu.com?a=b";
     * Map{@code <String, String>} keyAndArrayMap = new LinkedHashMap{@code <String, String>}();
     * 
     * keyAndArrayMap.put("province", "??");
     * keyAndArrayMap.put("city", "?");
     * 
     * LOGGER.info(ParamUtil.addParameterSingleValueMap(beforeUrl, keyAndArrayMap, UTF8));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code www.baidu.com?a=b&province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
     * </pre>
     * 
     * </blockquote>
     *
     * @param uriString
     *            the uri string
     * @param singleValueMap
     *            singleValueMap param name value 
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>uriString</code> nullempty, {@link StringUtils#EMPTY}<br>
     * @see #addParameterArrayValueMap(String, Map, String)
     */
    public static String addParameterSingleValueMap(String uriString, Map<String, String> singleValueMap,
            String charsetType) {
        return addParameterArrayValueMap(uriString, MapUtil.toArrayValueMap(singleValueMap), charsetType);
    }

    /**
     * ?,uri????,?.
     * 
     * <p>
     *  ?<code>queryString</code> ?,??map,?? <code>arrayValueMap</code>;<br>
     *  {@link LinkedHashMap},??map?
     * </p>
     * 
     * <h3>1:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String beforeUrl = "www.baidu.com";
     * Map{@code <String, String[]>} keyAndArrayMap = new LinkedHashMap{@code <String, String[]>}();
     * 
     * keyAndArrayMap.put("receiver", new String[] { "", "feilong" });
     * keyAndArrayMap.put("province", new String[] { "??" });
     * keyAndArrayMap.put("city", new String[] { "?" });
     * LOGGER.info(ParamUtil.addParameterArrayValueMap(beforeUrl, keyAndArrayMap, UTF8));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code www.baidu.com?receiver=%E9%91%AB%E5%93%A5&receiver=feilong&province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
     * </pre>
     * 
     * </blockquote>
     * 
     * 
     * <h3>2:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String beforeUrl = "www.baidu.com?a=b";
     * Map{@code <String, String[]>} keyAndArrayMap = new LinkedHashMap{@code <String, String[]>}();
     * keyAndArrayMap.put("province", new String[] { "??" });
     * keyAndArrayMap.put("city", new String[] { "?" });
     * LOGGER.info(ParamUtil.addParameterArrayValueMap(beforeUrl, keyAndArrayMap, UTF8));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code www.baidu.com?a=b&province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
     * </pre>
     * 
     * </blockquote>
     *
     * @param uriString
     *            the uri string
     * @param arrayValueMap
     *            the name and array value map
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return ?,uri????,?<br>
     *          <code>uriString</code> nullempty, {@link StringUtils#EMPTY}<br>
     *          <code>arrayValueMap</code> nullempty, <code>uriString</code><br>
     * @see #addParameterArrayValueMap(String, String, Map, String)
     * @since 1.4.0
     */
    public static String addParameterArrayValueMap(String uriString, Map<String, String[]> arrayValueMap,
            String charsetType) {
        return addParameterArrayValueMap(uriString, URIUtil.getQueryString(uriString), arrayValueMap, charsetType);
    }

    /**
     * {@code a=1&b=2}????map (charsetType?nullempty  keyvalue).
     * 
     * <p>
     *  {@link LinkedHashMap},map?? <code>queryString</code> ??
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String queryString = "{@code sec_id=MD5&format=xml&sign=cc945983476d615ca66cee41a883f6c1&v=2.0&req_data=%3Cauth_and_execute_req%3E%3Crequest_token%3E201511191eb5762bd0150ab33ed73976f7639893%3C%2Frequest_token%3E%3C%2Fauth_and_execute_req%3E&service=alipay.wap.auth.authAndExecute&partner=2088011438559510}";
     * LOGGER.info(JsonUtil.format(ParamUtil.toSingleValueMap(queryString, UTF8)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "sec_id": "MD5",
     * "format": "xml",
     * "sign": "cc945983476d615ca66cee41a883f6c1",
     * "v": "2.0",
     * "req_data":"%3Cauth_and_execute_req%3E%3Crequest_token%3E201511191eb5762bd0150ab33ed73976f7639893%3C%2Frequest_token%3E%3C%2Fauth_and_execute_req%3E",
     * "service": "alipay.wap.auth.authAndExecute",
     * "partner": "2088011438559510"
     * }
     * </pre>
     * 
     * </blockquote>
     *
     * @param queryString
     *            the query string
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>queryString</code> nullempty, {@link Collections#emptyMap()}<br>
     * @see #toSafeArrayValueMap(String, String)
     * @since 1.4.0
     */
    public static Map<String, String> toSingleValueMap(String queryString, String charsetType) {
        return MapUtil.toSingleValueMap(toSafeArrayValueMap(queryString, charsetType));
    }

    /**
     * {@code a=1&b=2}????map (charsetType ?nullempty  keyvalue).
     * 
     * <p>
     *  {@link LinkedHashMap},map?? <code>queryString</code> ??
     * </p>
     * 
     * <p>
     * ??:?? {@code &} , ?keyvalue  = ?
     * </p>
     * 
     * <h3>1:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * LOGGER.info(JsonUtil.format(ParamUtil.toSafeArrayValueMap("{@code a=1&b=2&a=5}", UTF8)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     {"a": [
             "1",
             "5"
         ],
         "b": ["2"]
      }
     * </pre>
     * 
     * <hr>
     * 
     * <pre class="code">
     * LOGGER.info(JsonUtil.format(ParamUtil.toSafeArrayValueMap("{@code a=&b=2&a}", UTF8)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     {"a": [
         "",
         ""
     ],
      "b": ["2"]
     }
     * </pre>
     * 
     * </blockquote>
     *
     * @param queryString
     *            {@code a=1&b=2}?,?{@code a=1&a=1}?, map
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>queryString</code> nullempty, {@link Collections#emptyMap()}<br>
     * @see org.apache.commons.lang3.ArrayUtils#add(Object[], Object)
     * @see com.feilong.core.lang.StringUtil#split(String, String)
     * @since 1.4.0
     */
    static Map<String, String[]> toSafeArrayValueMap(String queryString, String charsetType) {
        if (isNullOrEmpty(queryString)) {
            return Collections.emptyMap();
        }

        String[] nameAndValueArray = StringUtil.split(queryString, AMPERSAND);
        int length = nameAndValueArray.length;

        Map<String, String[]> safeArrayValueMap = newLinkedHashMap(length);// LinkedHashMap ???
        for (int i = 0; i < length; ++i) {
            String[] tempArray = nameAndValueArray[i].split("=", 2);

            String key = decodeAndEncode(tempArray[0], charsetType);
            String value = tempArray.length == 2 ? tempArray[1] : EMPTY;//??,???,???
            value = decodeAndEncode(value, charsetType);

            safeArrayValueMap.put(key, ArrayUtils.add(safeArrayValueMap.get(key), value));
        }
        return safeArrayValueMap;
    }

    /**
     * map?? queryString.
     * 
     * <p>
     * queryString??,singleValueMap key?,? {@link TreeMap},{@link LinkedHashMap}??
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * map,
     * 
     * <pre class="code">
     * Map{@code <String, String[]>} keyAndArrayMap = new HashMap{@code <String, String[]>}();
     * keyAndArrayMap.put("name", new String[] { "jim", "feilong", "" });
     * keyAndArrayMap.put("age", new String[] { "18" });
     * keyAndArrayMap.put("love", new String[] { "sanguo" });
     * </pre>
     * 
     * :
     * 
     * <pre class="code">
     * LOGGER.info(ParamUtil.toSafeQueryString(keyAndArrayMap, UTF8));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code love=sanguo&age=18&name=jim&name=feilong&name=%E9%91%AB%E5%93%A5}
     * </pre>
     * 
     * :
     * 
     * <pre class="code">
     * LOGGER.info(ParamUtil.toSafeQueryString(keyAndArrayMap, null));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code love=sanguo&age=18&name=jim&name=feilong&name=}
     * </pre>
     * 
     * </blockquote>
     *
     * @param arrayValueMap
     *             <code>request.getParamMap</code>
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>arrayValueMap</code> nullempty, {@link StringUtils#EMPTY}<br>
     * @see #toQueryStringUseArrayValueMap(Map)
     * @since 1.4.0
     */
    public static String toSafeQueryString(Map<String, String[]> arrayValueMap, String charsetType) {
        return toQueryStringUseArrayValueMap(toSafeArrayValueMap(arrayValueMap, charsetType));
    }

    //*********************************************************************************************

    /**
     *  <code>singleValueMap</code> ?<code>?</code> <code>queryString</code> .
     * 
     * <p style="color:red">
     * ?(?),? <b>??</b>,?encode?,<b></b>
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * <p>
     * <code>singleValueMap</code>  {@link SortUtil#sortByKeyAsc(Map)} ?,<br>
     * ?mapkeyvalue = ? ,<br>
     * ??entry?{@code &} ?,? url queryString,
     * </p>
     * 
     * </blockquote>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String>} map = new HashMap{@code <String, String>}();
     * map.put("service", "create_salesorder");
     * map.put("_input_charset", "gbk");
     * map.put("totalActual", "210.00");
     * map.put("province", "??");
     * map.put("city", "?");
     * map.put("district", "");
     * map.put("address", "??888888?");
     * LOGGER.debug(ParamUtil.toNaturalOrderingQueryString(map));
     * </pre>
     * 
     *  :
     * 
     * <pre class="code">
     * {@code _input_charset=gbk&address=??888888?&city=?&district=&province=??&service=create_salesorder&totalActual=210.00}
     * </pre>
     * 
     * </blockquote>
     * 
     * <h3>  key value?:</h3>
     * 
     * <blockquote>
     * <p>
     *  <code>singleValueMap</code> ,key<code>null</code>, {@link NullPointerException}, 
     * {@link SortUtil#sortByKeyAsc(Map)} ?, {@link TreeMap} ??null key;<br>
     * 
     * value <code>null</code>, {@link StringUtils#EMPTY} 
     * </p>
     * 
     * </blockquote>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * Map{@code <String, String>} map = new HashMap{@code <String, String>}();
     * map.put("service", null);
     * map.put("totalActual", "210.00");
     * map.put("province", "??");
     * 
     * LOGGER.debug(ParamUtil.toNaturalOrderingQueryString(map));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code province=??&service=&totalActual=210.00}
     * </pre>
     * 
     * </blockquote>
     * 
     * @param singleValueMap
     *            ???
     * @return  <code>singleValueMap</code> nullempty, {@link StringUtils#EMPTY}<br>
     *         ?<code>singleValueMap</code>??, {@link #toQueryStringUseSingleValueMap(Map)}
     * @see #toSafeQueryString(Map, String)
     * @since 1.4.0
     */
    public static String toNaturalOrderingQueryString(Map<String, String> singleValueMap) {
        return null == singleValueMap ? EMPTY : toQueryStringUseSingleValueMap(sortByKeyAsc(singleValueMap));
    }

    /**
     * <code>singleValueMap</code>? queryString.
     * 
     * <p>
     * ??mapkey value  <code>singleValueMap</code>? ?,? url queryString,
     * ,???<code>param Name=name</code>,<code>param Value=zhangfei</code>, <code>name=zhangfei</code>
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String>} singleValueMap = new LinkedHashMap{@code <String, String>}();
     * 
     * singleValueMap.put("province", "??");
     * singleValueMap.put("city", "?");
     * 
     * LOGGER.info(ParamUtil.joinSingleValueMap(singleValueMap));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code province=??&city=?}
     * </pre>
     * 
     * </blockquote>
     *
     * @param singleValueMap
     *            the params map
     * @return <code>singleValueMap</code> nullempty, {@link StringUtils#EMPTY} <br>
     *         ?  <code>singleValueMap</code> ? {@link MapUtil#toArrayValueMap(Map)},? {@link #toQueryStringUseArrayValueMap(Map)}
     * @see MapUtil#toArrayValueMap(Map)
     * @see #toQueryStringUseArrayValueMap(Map)
     * @see <a href="http://www.leveluplunch.com/java/examples/build-convert-map-to-query-string/">build-convert-map-to-query-string</a>
     * @since 1.5.5
     */
    public static String toQueryStringUseSingleValueMap(Map<String, String> singleValueMap) {
        return toQueryStringUseArrayValueMap(MapUtil.toArrayValueMap(singleValueMap));
    }

    /**
     * ??mapkey value ?,? url queryString.
     * 
     * <h3>?:</h3>
     * 
     * <blockquote>
     * <ul>
     * <li><span style="color:red">?encode?</span>,</li>
     * <li>map key??,???;,??</li>
     * </ul>
     * </blockquote>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String[]>} keyAndArrayMap = new LinkedHashMap{@code <String, String[]>}();
     * 
     * keyAndArrayMap.put("province", new String[] { "??", "?" });
     * keyAndArrayMap.put("city", new String[] { "?" });
     * LOGGER.info(ParamUtil.joinArrayValueMap(keyAndArrayMap));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {@code province=??&province=?&city=?}
     * </pre>
     * 
     * </blockquote>
     *
     * @param arrayValueMap
     *            the array value map
     * @return  <code>arrayValueMap</code>  NullEmpty, {@link StringUtils#EMPTY}<br>
     *         ? <code>arrayValueMap</code> ?QueryString
     * @see #joinParamNameAndValues(String, String[])
     * @see <a href="http://www.leveluplunch.com/java/examples/build-convert-map-to-query-string/">build-convert-map-to-query-string</a>
     * @since 1.5.5
     */
    public static String toQueryStringUseArrayValueMap(Map<String, String[]> arrayValueMap) {
        if (isNullOrEmpty(arrayValueMap)) {
            return EMPTY;
        }

        int i = 0;
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String[]> entry : arrayValueMap.entrySet()) {
            sb.append(joinParamNameAndValues(entry.getKey(), entry.getValue()));
            if (i != arrayValueMap.size() - 1) {// ?& ?
                sb.append(AMPERSAND);
            }
            ++i;
        }
        return sb.toString();
    }

    /**
     * ?keysvalue,?(<span style="color:red">?</span>).
     * 
     * <p>
     * includeKeys?,? () ??<br>
     * mapvaluenull,{@link StringUtils#EMPTY}
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String>} map = new HashMap{@code <String, String>}();
     * map.put("service", "create_salesorder");
     * map.put("paymentType", "unionpay_mobile");
     * 
     * LOGGER.info(ParamUtil.joinValues(map, "service", "paymentType"));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * create_salesorderunionpay_mobile
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param singleValueMap
     *            the map
     * @param includeKeys
     *            ?key
     * @return  <code>singleValueMap</code> null, {@link NullPointerException}<br>
     *          <code>includeKeys</code> nullempty, {@link StringUtils#EMPTY}<br>
     *         ? <code>includeKeys</code>,? <code>singleValueMap</code>?,?;<br>
     *         ( <code>singleValueMap</code>keynull,{@link StringUtils#defaultString(String)} ?{@link StringUtils#EMPTY})<br>
     * @see org.apache.commons.lang3.StringUtils#defaultString(String)
     * @since 1.5.5
     */
    @SafeVarargs
    public static <K> String joinValuesOrderByIncludeKeys(Map<K, String> singleValueMap, K... includeKeys) {
        Validate.notNull(singleValueMap, "singleValueMap can't be null!");
        if (isNullOrEmpty(includeKeys)) {
            return EMPTY;
        }
        StringBuilder sb = new StringBuilder();
        for (K key : includeKeys) {//??
            //?: valuenull,StringBuilder "null" , ?  java.lang.AbstractStringBuilder#append(String)
            sb.append(StringUtils.defaultString(singleValueMap.get(key)));
        }
        return sb.toString();
    }
    //*******************************************************************************************

    /**
     *  parameter array value map.
     * 
     * <p>
     *  <code>queryString</code> ??,??map,?? <code>arrayValueMap</code>;<br>
     *  {@link LinkedHashMap},??map?
     * </p>
     * 
     * @param uriString
     *            the uri string
     * @param queryString
     *            the query
     * @param arrayValueMap
     *            the name and array value map
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return the string
     * @since 1.4.0
     */
    static String addParameterArrayValueMap(String uriString, String queryString,
            Map<String, String[]> arrayValueMap, String charsetType) {
        Map<String, String[]> safeArrayValueMap = ObjectUtils.defaultIfNull(arrayValueMap,
                Collections.<String, String[]>emptyMap());

        Map<String, String[]> arrayParamValuesMap = newLinkedHashMap(safeArrayValueMap.size());
        //???queryString map
        if (isNotNullOrEmpty(queryString)) {
            arrayParamValuesMap.putAll(toSafeArrayValueMap(queryString, null));
        }
        arrayParamValuesMap.putAll(safeArrayValueMap);
        return combineUrl(URIUtil.getFullPathWithoutQueryString(uriString), arrayParamValuesMap, charsetType);
    }

    /**
     * To safe array value map.
     * 
     * <p>
     *  {@link LinkedHashMap},??map?
     * </p>
     *
     * @param arrayValueMap
     *            the array value map
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>arrayValueMap</code> nullempty, {@link Collections#emptyMap()}<br>
     */
    private static Map<String, String[]> toSafeArrayValueMap(Map<String, String[]> arrayValueMap,
            String charsetType) {
        if (isNullOrEmpty(arrayValueMap)) {
            return Collections.emptyMap();
        }
        Map<String, String[]> safeArrayValueMap = newLinkedHashMap(arrayValueMap.size()); // LinkedHashMap,??map?
        for (Map.Entry<String, String[]> entry : arrayValueMap.entrySet()) {
            String key = entry.getKey();
            String[] paramValues = entry.getValue();
            if (isNullOrEmpty(paramValues)) {
                LOGGER.warn("the param key:[{}] value is null", key);
                paramValues = EMPTY_STRING_ARRAY;// empty,??
            }
            safeArrayValueMap.put(decodeAndEncode(key, charsetType), toSafeValueArray(paramValues, charsetType));
        }
        return safeArrayValueMap;
    }

    /**
     * To safe value array.
     *
     * @param paramValues
     *            the param values
     * @param charsetType
     *            the charset type
     * @return the string[]
     * @since 1.6.2
     */
    private static String[] toSafeValueArray(String[] paramValues, String charsetType) {
        if (isNullOrEmpty(charsetType)) {
            return paramValues;
        }
        List<String> paramValueList = new ArrayList<String>();
        for (String value : paramValues) {
            paramValueList.add(decodeAndEncode(value, charsetType));
        }
        return ConvertUtil.toArray(paramValueList, String.class);
    }

    /**
     * ??.
     * 
     * <p>
     * ,??? {@code paramName=name}, {@code paramValues  zhangfei,guanyu},{@code name=zhangfei&name=guanyu}
     * </p>
     * 
     * <h3>?:</h3>
     * <blockquote>
     * <ol>
     * <li>paramName ?  {@link StringUtils#defaultString(String)}???</li>
     * </ol>
     * </blockquote>
     *
     * @param paramName
     *            ???
     * @param paramValues
     *            ?
     * @return the string
     * @see java.lang.AbstractStringBuilder#append(String)
     * @see org.apache.commons.lang3.StringUtils#defaultString(String)
     * @see "org.springframework.web.servlet.view.RedirectView#appendQueryProperties(StringBuilder,Map, String)"
     * @since 1.4.0
     */
    private static String joinParamNameAndValues(String paramName, String[] paramValues) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0, j = paramValues.length; i < j; ++i) {
            //?: value null ,StringBuilder "null" , ?  java.lang.AbstractStringBuilder#append(String)
            sb.append(StringUtils.defaultString(paramName)).append("=")
                    .append(StringUtils.defaultString(paramValues[i]));
            if (i != j - 1) {// ?& ?
                sb.append(AMPERSAND);
            }
        }
        return sb.toString();
    }

    /**
     * ?queryString()?(?);chromequery encoded???;ie????.
     * 
     * <p>
     * ??encoded,decode?encode;
     * </p>
     * 
     * <p>
     * ?decode(query,charsetType),? =
     * </p>
     *
     * @param value
     *            the value
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>value</code> nullempty, {@link StringUtils#EMPTY}<br>
     *         <code>charsetType</code> nullempty, <code>value</code><br>
     *         ? {@link URIUtil#decode(String, String)} ? {@link URIUtil#encode(String, String)}
     * @see <a
     *      href="http://stackoverflow.com/questions/15004593/java-request-getquerystring-value-different-between-chrome-and-ie-browser">
     *      java-request-getquerystring-value-different-between-chrome-and-ie-browser</a>
     * @since 1.4.0
     */
    private static String decodeAndEncode(String value, String charsetType) {
        if (isNullOrEmpty(value)) {
            return EMPTY;
        }
        return isNullOrEmpty(charsetType) ? value : URIUtil.encode(URIUtil.decode(value, charsetType), charsetType);
    }

    /**
     * url.
     *
     * @param beforePathWithoutQueryString
     *            the before path without query string
     * @param arrayValueMap
     *            the array value map
     * @param charsetType
     *            ??, {@link CharsetType}<br>
     *            <span style="color:green">null empty,?,?</span><br>
     *            ??,??,ie?chrome? url ,?
     * @return  <code>beforePathWithoutQueryString</code> nullempty, {@link StringUtils#EMPTY}<br>
     *         <code>arrayValueMap</code> nullempty, <code>beforePathWithoutQueryString</code>
     * @since 1.4.0
     */
    static String combineUrl(String beforePathWithoutQueryString, Map<String, String[]> arrayValueMap,
            String charsetType) {
        if (isNullOrEmpty(beforePathWithoutQueryString)) {
            return EMPTY;
        }
        if (isNullOrEmpty(arrayValueMap)) {//? return
            return beforePathWithoutQueryString;
        }

        StringBuilder sb = new StringBuilder();
        sb.append(beforePathWithoutQueryString);
        sb.append(QUESTIONMARK);
        sb.append(toSafeQueryString(arrayValueMap, charsetType));

        return sb.toString();
    }
}