package cn.van333.mt2ql.wxMessage.utils;

/**
 * @author Leo
 * @version 1.0
 * @create 2023/12/21 0021 上午 11:27
 * @description： 旗云的 Util 用习惯了 用一下
 */

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.HtmlUtils;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * 最基础的工具类
 */
public class
Util {

    private static final Logger log = LoggerFactory.getLogger(Util.class);

    /**
     * byte数组倒序
     *
     * @param bytes
     * @return
     */
    public static final byte[] byteArrayDesc(byte[] bytes) {
        if (Util.isEmpty(bytes)) {
            return new byte[0];
        }
        int len = bytes.length;
        byte[] tmp = new byte[len];
        for (int i = len - 1; i >= 0; i--) {
            tmp[len - i - 1] = bytes[i];
        }
        return tmp;

    }

    /**
     * 打印程序耗时
     *
     * @param msg
     */
    public static final void consume(long start, long end, String msg) {
        System.err.println(String.format("%s consume times: %s=%s-%s", msg, (end - start), end, start));
    }

    /**
     * 将非法且不可见的XML字符转为空格（空格的的ascii码是32）
     *
     * @param bts byte[]
     * @return 转换后的 byte[]
     */
    public static final byte[] convertInvalidInvisibleToSpace(byte[] bytes) {
        if (Util.isEmpty(bytes)) {
            return new byte[0];
        }
        byte[] tmp = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            if (b >= 0 && b <= 8 || b >= 14 && b <= 31 || b == 11 || b == 12 || b == 127) {
                tmp[i] = 32;
            } else {
                tmp[i] = bytes[i];
            }
        }
        return tmp;
    }

    /**
     * 将非法且不可见的XML字符转为空格
     */
    public static final String convertInvalidInvisibleToSpace(String str) {
        if (Util.isEmpty(str)) {
            return "";
        }
        return new String(Util.convertInvalidInvisibleToSpace(str.getBytes()));
    }

    /**
     * 将非法且不可见的XML字符转为空格
     *
     * @param bts byte[]
     * @return 转换后的 byte[]
     */
    public static final byte[] convertInvalidInvisibleXml(byte[] bytes) {
        if (Util.isEmpty(bytes)) {
            return new byte[0];
        }
        byte[] tmp = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            if (b >= 0 && b <= 8)// \\x00-\\x08
            {
                tmp[i] = 32;
            } else if (b >= 14 && b <= 31)// \\x0e-\\x1f
            {
                tmp[i] = 32;
            } else if (b == 11 || b == 12 || b == 127)// \\x0b-\\x0c and \\x7f
            {
                tmp[i] = 32;
            } else {
                tmp[i] = bytes[i];
            }
        }
        return tmp;
    }

    /**
     * 删除临时文件.
     *
     * @param dir
     */
    public static final void deleteTempFile(String dir) {
        File tf = new File(dir);
        String[] childs = tf.list();
        for (String child : childs) {
            new File(dir + File.separator + child).delete();
        }
    }

    /**
     * 读取文件路径，返回DataSource容器
     *
     * @param attFiles
     * @return
     */
    //public static final List<DataSource> fileDirsToDatasource(List<File> attFiles) {
    //    DataSource dc = null;
    //    List<DataSource> dcs = new ArrayList<DataSource>();
    //    if (isNotEmpty(attFiles)) {
    //        for (File file : attFiles) {
    //            dc = new FileDataSource(file);
    //            dcs.add(dc);
    //        }
    //    }
    //    return dcs;
    //}

    /**
     * 得到数组中非空元素的数量
     *
     * @param arr
     * @return
     */
    public static final int getArraySizeWithoutEmptyElement(String[] arr) {
        if (Util.isEmpty(arr)) {
            return 0;
        }
        int count = 0;
        for (String s : arr) {
            if (!Util.isEmpty(s)) {
                count++;
            }
        }
        return count;
    }

    /**
     * 指定cookie的key，获取对应的value。
     *
     * @param request
     * @param key     cookie key
     * @return cookie value
     */
    //public static final String getCookieValue(HttpServletRequest request, String key) {
    //    if (Util.isAnyEmpty(key, request)) {
    //        return "";
    //    }
    //    Cookie[] cookies = request.getCookies();
    //    if (Util.isNotEmpty(cookies)) {
    //        for (Cookie c : cookies) {
    //            if (key.equals(c.getName())) {
    //                if (Util.isNotEmpty(c.getValue())) {
    //                    return c.getValue();
    //                }
    //                break;
    //            }
    //        }
    //    }
    //    return "";
    //}

    /**
     * @param response
     * @param key      键
     * @param value    值
     * @param maxAge   存活时间，默认-1
     * @param path     有效域，默认/
     */
    //public static void setCookie(HttpServletResponse response, String key, String value, Integer maxAge, String path) {
    //    Cookie cookie = new Cookie(key, value);
    //
    //    // 设置浏览器的cookie保留时间，单位秒。负值标识浏览器关闭即删除cookie，0表示马上删除。
    //    // 这里设置成一年，如果想控制登录超时，可以用ADMIN_SESSION_TIMEOUT在t_parameter中设置redis的超时时间，统一后台控制。
    //    if (Util.isEmpty(maxAge)) {
    //        cookie.setMaxAge(-1);
    //    } else {
    //        cookie.setMaxAge(maxAge);
    //    }
    //
    //    if (Util.isEmpty(path)) {
    //        cookie.setPath("/");
    //    } else {
    //        cookie.setPath(path);
    //    }
    //
    //    // 这两个参数是因为应付漏洞扫描加入的。
    //    // 不允许前端javaScript显性读取
    //    // cookie.setHttpOnly(true);
    //    // 只允许https请求时提交给服务器，也就是说http（测试环境）服务器端读取不到cookie。
    //    // cookie.setSecure(true);
    //
    //    response.addCookie(cookie);
    //}

    /**
     * 将InputStream流写入一个File对象中返回
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    //public static final File inputStreamToFile(InputStream in, String name) throws IOException {
    //    if (Util.isEmpty(in)) {
    //        return null;
    //    }
    //    if (Util.isEmpty(name)) {
    //        name = Util.times() + "";
    //    }
    //    File f = new File(name);
    //    FileUtils.copyInputStreamToFile(in, f);
    //    return f;
    //}

    /**
     * 判断送入的字符串是否全部为中文
     *
     * @param name 需判断的字符串
     * @return 判断结果返回 true为中文 false为非中文
     */
    public static final boolean isChinese(String str) {
        if (Util.isEmpty(str)) {
            return false;
        }
        str = str.replaceAll("[\u4e00-\u9FFF]", "");
        if (Util.isEmpty(str)) {
            return true;
        }
        return false;
    }

    /**
     * 判断一个对象是否为空或者长度为0时返回true，否则返回false String:null或length=0返回true
     * List,Set,Map:null或size=0返回true 其他null返回true
     *
     * @param o
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static final boolean isEmpty(Object o) {
        if (null == o) {
            return true;
        }
        if (o instanceof String) {
            return (0 == ((String) o).trim().length());
        }
        if (o instanceof List) {
            return (0 == ((List) o).size());
        }
        if (o instanceof Set) {
            return (0 == ((Set) o).size());
        }
        if (o instanceof Map) {
            return (0 == ((Map) o).size());
        }
        if (o instanceof Object[]) {
            return (0 == ((Object[]) o).length);
        }
        return false;
    }

    /**
     * 判断一个对象不为null，并且长度不为0时返回true，否则返回false 规则参考Util.isEmpty()
     *
     * @param o
     * @return
     * @see #isEmpty(Object o)
     */
    public static final boolean isNotEmpty(Object o) {
        return !isEmpty(o);
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final String getNotNull(String t) {
        return isEmpty(t) ? new String("") : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final Character getNotNull(Character t) {
        return isEmpty(t) ? new Character('\0') : t;
    }

    /**
     * 获取非空对象
     *
     * @param t
     * @return
     */
    public static final Byte getNotNull(Byte t) {
        byte b = 0;
        return isEmpty(t) ? b : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final Short getNotNull(Short t) {
        short s = 0;
        return isEmpty(t) ? s : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final Integer getNotNull(Integer t) {
        return isEmpty(t) ? new Integer(0) : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final Long getNotNull(Long t) {
        return isEmpty(t) ? new Long(0) : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final Float getNotNull(Float t) {
        return isEmpty(t) ? new Float(0) : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static final Double getNotNull(Double t) {
        return isEmpty(t) ? new Double(0) : t;
    }

    /**
     * 获取非空对象，若空返回对象类型默认值
     *
     * @param t
     * @return
     */
    public static BigDecimal getNotNull(BigDecimal t) {
        return isEmpty(t) ? new BigDecimal(0) : t;
    }

    /**
     * 倒序行读文件内容（确保文件为UTF-8编码，才能显示中文）。
     *
     * @param filename          文件完整路径（确保文件必须存在并且是文件而不是路径）
     * @param seekIndex         从该字节位置往前读取，初始为0，输入null时从最后一个字节往前读取
     * @param howManyLineToRead 读取行数
     * @param isNewline         返回的内容是否换行
     * @return 返回指针位置(下标0 ， 用于下次继续读取 ） _读取内容 ( 下标1)
     * @throws BusinessException
     */
    //public static final List<String> readLineDesc(String filename, Long seekIndex, int howManyLineToRead,
    //                                              Boolean isNewline) throws BusinessException {
    //
    //    if (Util.isEmpty(filename)) {
    //        return null;
    //    }
    //
    //    if (seekIndex != null && seekIndex <= 0 || howManyLineToRead <= 0) {
    //        return null;
    //    }
    //
    //    RandomAccessFile rf = null;
    //    StringBuffer result = new StringBuffer("");
    //    List<String> temResult = new ArrayList<String>();
    //    int lineCount = 0;
    //    List<String> seekIndex_resultStr = null;
    //    String r = "";
    //    if (isNewline) {
    //        r = "\n";
    //    }
    //
    //    try {
    //        rf = new RandomAccessFile(filename, "r");
    //        // 计算文件指针位置，默认是文件字节数-1，即从最后一个字节开始
    //        long nextend = 0L;
    //        if (seekIndex == null) {
    //            // 得到文件最后一个字节的指针，指向该字节下方
    //            nextend = rf.length() - 1;
    //        } else {
    //            nextend = seekIndex;
    //        }
    //
    //        int c = -1;
    //        ByteArrayOutputStream out = new ByteArrayOutputStream();
    //        while (nextend >= 0 && howManyLineToRead > lineCount) {
    //            rf.seek(nextend);
    //            // 读取指针指向的字节
    //            c = rf.read();
    //            // 如果该字节的ascii码与换行符的ascii码相等
    //            if ((c == '\n') && nextend != 0) {
    //                lineCount++;
    //                String line = Util.trim(new String(byteArrayDesc(out.toByteArray())));
    //                if (Util.isNotEmpty(line)) {
    //                    temResult.add(line + r);
    //                }
    //                out = new ByteArrayOutputStream();
    //            } else {
    //                out.write(c);
    //            }
    //            if (nextend == 0) {// 当文件指针退至文件开始处，输出第一行
    //                String line = Util.trim(new String(byteArrayDesc(out.toByteArray())));
    //                if (Util.isNotEmpty(line)) {
    //                    temResult.add(line + r);
    //                }
    //                log.info("Have seek to the first byte.");
    //                break;
    //            }
    //            nextend--;
    //        }
    //        seekIndex_resultStr = new ArrayList<String>();
    //        seekIndex_resultStr.add(nextend + "");
    //        for (int i = temResult.size() - 1; i >= 0; i--) {
    //            if (i == 0) {
    //                result.append(temResult.get(i).trim());
    //            } else {
    //                result.append(temResult.get(i));
    //            }
    //        }
    //        seekIndex_resultStr.add(result.toString());
    //        return seekIndex_resultStr;
    //    } catch (FileNotFoundException e) {
    //        throw new BusinessException("Can't locate file " + filename + ",", e);
    //    } catch (IOException e) {
    //        throw new BusinessException("Read data from file " + filename + " failed,", e);
    //    } finally {
    //        try {
    //            if (rf != null) {
    //                rf.close();
    //            }
    //        } catch (IOException e) {
    //            throw new BusinessException("Close file " + filename + " failed,", e);
    //        }
    //    }
    //}

    /**
     * 获取当前时间毫秒数
     *
     * @return
     */
    public static final long times() {
        return System.currentTimeMillis();
    }

    /**
     * 强化trim()
     *
     * @param val
     * @return
     */
    public static final String trim(String val) {
        if (isEmpty(val)) {
            return "";
        } else {
            return val.trim();
        }
    }

    /**
     * 将File f对象写到服务器本地
     *
     * @param f
     * @param dir 服务器本地路径，可以是一个目录路径，也可以是一个文件路径 如果是目录路径，此目录若不存在，程序自动创建
     *            如果是文件路径，程序会自动创建此文件，包括不存在的目录，并将File
     *            f写入此文件中，注意，文件路径必须有文件后缀，否则被认为是目录路径 如果是目录路径，文件名是f.getName();
     *            如果是文件路径，文件名为路径中指定的文件名；
     *            路径如果不存在，创建的时候，如果此路径为绝对路径，那么创建相应的绝对路径，如果是相对路径，那么相对于用户的user目录，参考File
     *            getAbsolutePath()方法
     * @throws IOException
     * @throws BusinessException
     */
    //public static final void writeFileToLocalDir(File f, String dir) throws IOException, BusinessException {
    //    if (Util.isEmpty(f)) {
    //        throw new BusinessException("Util.writeFileToLocalDir()'s File f is null.");
    //    }
    //    if (Util.isEmpty(dir)) {
    //        throw new BusinessException("Util.writeFileToLocalDir()'s File dir is empty.");
    //    }
    //    File dirf = new File(dir);
    //    if (!dirf.exists()) {
    //        String[] dirArr = dirf.getAbsolutePath().split("\\" + File.separator);
    //        String lastDir = dirArr[dirArr.length - 1];
    //        if (!lastDir.contains(".")) {
    //            dirf = new File(dirf.getAbsolutePath() + File.separator + f.getName());
    //        }
    //    } else if (dirf.exists() && dirf.isDirectory()) {
    //        dirf = new File(dirf.getAbsolutePath() + File.separator + f.getName());
    //    }
    //    FileUtils.writeByteArrayToFile(dirf, FileUtils.readFileToByteArray(f));
    //}

    /**
     * 将字符串的首字母变小写
     *
     * @param s
     * @return
     */
    public static final String lowerCaseFirstChar(String s) {
        if (Util.isEmpty(s)) {
            return "";
        }
        return s.substring(0, 1).toString().toLowerCase() + s.substring(1).toString();
    }

    /**
     * 将字符串的首字母变大写
     *
     * @param s
     * @return
     */
    public static final String upperCaseFirstChar(String s) {
        if (Util.isEmpty(s)) {
            return "";
        }
        return s.substring(0, 1).toString().toUpperCase() + s.substring(1).toString();
    }

    /**
     * 只要其中一个对象是empty，返回true，否则返回false
     *
     * @param o
     * @return
     */
    public static final boolean isAnyEmpty(Object... o) {
        for (int i = 0; i < o.length; i++) {
            Object obj = o[i];
            if (Util.isEmpty(obj)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 所有对象都不为empty，返回true，否则返回false
     *
     * @param o
     * @return
     */
    public static final boolean isNotAnyEmpty(Object... o) {
        return !isAnyEmpty(o);
    }

    /**
     * 获取UUID，纳秒+uuid，用纳秒开头是为了让记录可以使用这个uuid进行排序
     */
    public static final String getUUID() {
        return System.nanoTime() + UUID.randomUUID().toString().replaceAll("\\-", "");
    }

    /**
     * 使用不可见字符串Constant.unsee0x01将字符串隔开，重新组成一串字符串
     *
     * @param strs
     * @return
     */
    public static final String joinStringWithUnseeChar(Collection<String> strs) {
        return joinStringWithSplitor(strs, String.valueOf((char) 0x01));
    }

    /**
     * 用任何分隔符将字符串容器中的字符串组成一串字符串。
     *
     * @param strs
     * @param splitor
     * @return
     */
    public static final String joinStringWithSplitor(Collection<String> strs, String splitor) {
        if (Util.isEmpty(strs)) {
            return "";
        }
        StringBuffer res = new StringBuffer("");
        for (String s : strs) {
            res.append(s + splitor);
        }
        if (Util.isNotEmpty(res.toString())) {
            return res.toString().substring(0, res.toString().length() - 1);
        }
        return res.toString();
    }

    /**
     * 使用不可见字符串Constant.unsee0x01将字符串隔开，重新组成一串字符串
     *
     * @param strs
     * @return
     */
    public static final String joinStringWithUnseeChar(String[] strs) {
        return joinStringWithSplitor(strs, String.valueOf((char) 0x01));
    }

    /**
     * 用任何分隔符将字符串数组中的字符串组成一串字符串。
     *
     * @param strs
     * @param splitor
     * @return
     */
    public static final String joinStringWithSplitor(String[] strs, String splitor) {
        if (Util.isEmpty(strs)) {
            return "";
        }
        StringBuffer res = new StringBuffer("");
        for (String s : strs) {
            res.append(s + splitor);
        }
        if (Util.isNotEmpty(res.toString())) {
            return res.toString().substring(0, res.toString().length() - 1);
        }
        return res.toString();
    }

    /**
     * 去除字符串数组中的空串
     *
     * @param split
     * @return
     */
    public static final String[] removeEmptyElements(String[] split) {
        List<String> l = new ArrayList<String>();
        for (String s : split) {
            if (Util.isNotEmpty(s)) {
                l.add(s);
            }
        }
        String[] res = new String[l.size()];
        return l.toArray(res);
    }

    /**
     * 去除队列中为null的元素
     *
     * @param list
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> removeNull(List<? extends T> list) {
        if (Util.isEmpty(list)) {
            return (List<T>) list;
        }
        list.removeAll(Collections.singleton(null));
        List<T> res = (List<T>) list;
        return res;
    }

    /**
     * 去除队列中为null的元素
     *
     * @param list
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> removeEmpty(List<? extends T> list) {
        if (Util.isEmpty(list)) {
            return (List<T>) list;
        }
        List<T> newList = new ArrayList<T>();
        for (T t : list) {
            if (Util.isNotEmpty(t)) {
                newList.add(t);
            }
        }
        return newList;
    }

    /**
     * 获取文件后缀
     *
     * @param fileName 文件名，File对象调用getName();
     * @return
     */
    public static final String getSuffix(String fileName) {
        if (Util.isEmpty(fileName)) {
            return "";
        }
        fileName = getRealFileName(fileName);
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 获取文件后缀（连后缀的最开始的“.”都不要了）
     *
     * @param fileName 文件名，File对象调用getName();
     * @return
     */
    public static final String getSuffixWithoutDot(String fileName) {
        if (Util.isEmpty(fileName)) {
            return "";
        }
        fileName = getRealFileName(fileName);
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    /**
     * 获取除掉后缀的文件名
     *
     * @param fileName 文件名，File对象调用getName();
     * @return
     */
    public static final String getFileNameIgnoreSuffix(String fileName) {
        if (Util.isEmpty(fileName)) {
            return "";
        }
        fileName = getRealFileName(fileName);
        return fileName.substring(0, fileName.lastIndexOf("."));
    }

    /**
     * 获取文件名，有些文件名是一串路径，有些是正确的文件名，这个方法做兼容。
     * 比如IE上传到后台接收到的MultipartFile的getOriginalFilename就是一串路径。
     *
     * @return 最终的文件名
     */
    public static final String getRealFileName(String fileName) {
        if (fileName.indexOf("/") != -1) {
            fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
        }
        if (fileName.indexOf("\\") != -1) {
            fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
        }
        return fileName;
    }

    /**
     * 删除第一个字符以及最后一个字符
     *
     * @return
     */
    public static final String removeFirstLastChar(String s) {
        if (Util.isEmpty(s)) {
            return "";
        }
        return s.substring(1, s.length() - 1);
    }

    /**
     * spring mvc专用下载
     *
     * @param response
     * @param request
     * @param name     客户端附件下载的名字
     * @param b        下载数据
     * @throws IOException
     * @throws BusinessException
     */
    //public static final ResponseEntity<byte[]> write(String name, byte[] b) throws IOException, BusinessException {
    //    try {
    //        HttpHeaders headers = new HttpHeaders();
    //        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    //        String downloadFileName = new String(name.getBytes(Constant.ENCODING), "iso-8859-1");// 设置编码
    //        headers.setContentDispositionFormData("attachment", downloadFileName);
    //        return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);
    //    } catch (Exception e) {
    //        throw new BusinessException(e);
    //    }
    //}

    /**
     * 将Map的value中的String[]变成以Constant.unsee0x01隔开的字符串。 原因是Controller不能传递Map<String,
     * String[]>的形式到WS层方法， 因为不想做那么复杂，当前只支持用j_m_m_xxx传递Map<String, model>
     * 的变量，所以当分页的时候，需要传递的searchParams就是Map<String, String[]>
     * 形式的，所以需要将Map里面的String[]变成字符串，从而整个变量就能使用j_map_xxx来传递
     *
     * @param j_map_searchParams
     * @return
     */
    //public static Map<String, String> convertMapArrayToStirng(Map<String, Object> j_map_searchParams) {
    //    Map<String, String> res = new HashMap<String, String>();
    //    if (Util.isEmpty(j_map_searchParams)) {
    //        return res;
    //    }
    //    for (Entry<String, Object> en : j_map_searchParams.entrySet()) {
    //        if (Util.isNotEmpty(en.getValue())) {
    //            if (en.getValue() instanceof String[]) {
    //                res.put(en.getKey(), Util.joinStringWithUnseeChar((String[]) en.getValue()));
    //            } else {
    //                res.put(en.getKey(), (String) en.getValue());
    //            }
    //        }
    //    }
    //    return res;
    //}

    /**
     * 格式化文件的大小，如果小于1024kb，直接显示kb，如果大于1024kb，换算成mb进行显示，以此类推。
     *
     * @param size 传一个b为单位的数字
     * @return
     */
    public static String formatFileSize(long size) {
        String fileSize = "";
        double kbNum = new BigDecimal(size).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
        if (1024 < kbNum) {
            double mbNum = new BigDecimal(kbNum).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP)
                    .doubleValue();
            if (1024 < mbNum) {
                double gbNum = new BigDecimal(mbNum).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP)
                        .doubleValue();
                fileSize = gbNum + "GB";
            } else {
                fileSize = mbNum + "MB";
            }
        } else {
            fileSize = kbNum + "KB";
        }
        return fileSize;
    }

    /**
     * 判断字符串中字母的个数
     */
    public static int getAlphaCharLength(String str) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) * 1 >= 65 && str.charAt(i) * 1 <= 90
                    || str.charAt(i) * 1 >= 97 && str.charAt(i) * 1 <= 122) {
                count++;
            }
        }
        return count;
    }

    /**
     * 判断字符串中数字个数
     */
    public static int getNumLength(String str) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) * 1 >= 48 && str.charAt(i) * 1 <= 57) {
                count++;
            }
        }
        return count;
    }

    ;

    /**
     * 判断字符串中特殊字符个数。
     */
    public static int getSpecialCharLength(String str) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) * 1 < 48 || str.charAt(i) * 1 > 57 && str.charAt(i) * 1 < 65
                    || str.charAt(i) * 1 > 90 && str.charAt(i) * 1 < 97 || str.charAt(i) * 1 > 122) {
                count++;
            }
        }
        return count;
    }

    ;

    /**
     * 判断字符串是否包含大小写。
     */
    public static boolean containBigAndSmall(String str) {
        boolean big = false;
        boolean small = false;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) * 1 >= 65 && str.charAt(i) * 1 <= 90) {
                big = true;
                break;
            }
        }
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) * 1 >= 97 && str.charAt(i) * 1 <= 122) {
                small = true;
                break;
            }
        }
        if (big && small) {
            return true;
        }
        return false;
    }

    /**
     * 简单混淆字符串中字符。 当前是0位和1位调换，最后两位字符调换。
     *
     * @param str
     * @return
     */
    public static String confusionStr(String str) {
        char[] c = str.toCharArray();
        char a = c[1];
        c[1] = c[0];
        c[0] = a;
        a = c[c.length - 1];
        c[c.length - 1] = c[c.length - 2];
        c[c.length - 2] = a;
        return new String(c);
    }

    /**
     * 获取某个范围的随机数。 如获取0-5的随机数，minRang:0，maxRang:5， 如获取5-50的随机数，minRang:5，maxRang:50
     *
     * @param minRang
     * @param maxRang
     * @return
     */
    public static int getRandomIntInRang(int minRang, int maxRang) {
        Random rand = new Random();
        int randNum = rand.nextInt(maxRang + 1) + minRang;
        return randNum;
    }

    /**
     * 随机获取a-z，0-9中的n个字符作为验证码。
     *
     * @return
     */
    public static String getVerifyCodeChars(int n) {
        char[] c = new char[]{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
                'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
        int randNum = -1;
        char[] resc = new char[n];
        for (int i = 0; i < n; i++) {
            randNum = getRandomIntInRang(0, 35);
            try {
                resc[i] = c[randNum];
            } catch (Exception e) {
                resc[i] = 'p';
                continue;
            }
        }
        return new String(resc);
    }

    /**
     * 随机获取0-9中的n个字符作为验证码。一般用作短信验证码时。
     *
     * @return
     */
    public static String getVerifyCodeCharsOnliNum(int n) {
        char[] c = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
        int randNum = -1;
        char[] resc = new char[n];
        for (int i = 0; i < n; i++) {
            randNum = getRandomIntInRang(0, 9);
            try {
                resc[i] = c[randNum];
            } catch (Exception e) {
                resc[i] = '8';
                continue;
            }
        }
        return new String(resc);
    }

    /**
     * 去除空元素后再join
     * 和javaScript的数组的join方法作用一样，主要用于将数组的元素用指定的连接字符相连成一行字符串，
     * 放在searchParams中的IN参数中进行查询，如["a", "b", 1]→"a,b,1"
     *
     * @param array
     * @param joinStr 链接字符串，可以是单个字符，也可以是多个字符的字符串，也可以是空串。
     * @return
     */
    public static <T> String joinIgnoreEmpty(List<T> array, String joinStr) {
        array = removeEmpty(array);
        return join(array, joinStr);
    }

    public static void main(String[] args) {
    }

    /**
     * 和javaScript的数组的join方法作用一样，主要用于将数组的元素用指定的连接字符相连成一行字符串，
     * 放在searchParams中的IN参数中进行查询，如["a", "b", 1]→"a,b,1"
     *
     * @param array
     * @param joinStr 链接字符串，可以是单个字符，也可以是多个字符的字符串，也可以是空串。
     * @return
     */
    public static <T> String join(List<T> array, String joinStr) {
        if (Util.isEmpty(array)) {
            return "";
        } else {
            StringBuffer sb = new StringBuffer("");
            for (T o : array) {
                String val = "";
                if (Util.isNotEmpty(o)) {
                    val = o.toString();
                }
                sb.append(val + joinStr);
            }
            if (Util.isNotEmpty(sb.toString())) {
                return sb.toString().substring(0, sb.toString().length() - joinStr.length());
            }
        }
        return "";
    }

    /**
     * 将字符串按照正则匹配分割
     */
    public static List<String> cut(String str, String regex) {
        List<String> list = new ArrayList<String>();
        if (Util.isEmpty(regex)) {
            return list;
        }
        String[] ss = str.split(regex);
        for (String s : ss) {
            list.add(s);
        }
        return list;
    }

    /**
     * 将字符串按指定分割符切分成多段并转成List
     *
     * @param string
     * @param splitStr
     * @return
     */
    public static List<String> convertToList(String string, String splitStr) {
        List<String> list = new ArrayList<String>();
        if (Util.isEmpty(string)) {
            return list;
        }
        String[] strs = string.split(splitStr);
        if (Util.isEmpty(strs)) {
            return list;
        }
        for (String str : strs) {
            list.add(str);
        }
        return list;
    }

    /**
     * HTML字符转义
     *
     * @return String 过滤后的字符串
     * @see 对输入参数中的敏感字符进行过滤替换,防止用户利用JavaScript等方式输入恶意代码
     * @see String input = <img src='http://t1.baidu.com/it/fm=0&gp=0.jpg'/>
     * @see HtmlUtils.htmlEscape(input); //from spring.jar
     * @see StringEscapeUtils.escapeHtml(input); //from commons-lang.jar
     * @see 尽管Spring和Apache都提供了字符转义的方法,但Apache的StringEscapeUtils功能要更强大一些
     * @see StringEscapeUtils提供了对HTML,Java,JavaScript, SQL ,XML等字符的转义和反转义
     * @see 但二者在转义HTML字符时,都不会对单引号和空格进行转义,而本方法则提供了对它们的转义
     */
    public static String htmlEscape(String input) {
        if (isEmpty(input)) {
            return input;
        }
        input = input.replaceAll("&", "&amp;");
        input = input.replaceAll("<", "&lt;");
        input = input.replaceAll(">", "&gt;");
        input = input.replaceAll(" ", "&nbsp;");
        input = input.replaceAll("'", "&#39;"); // IE暂不支持单引号的实体名称,而支持单引号的实体编号,故单引号转义成实体编号,其它字符转义成实体名称
        input = input.replaceAll("\"", "&quot;"); // 双引号也需要转义，所以加一个斜线对其进行转义
        input = input.replaceAll("\n", "<br/>"); // 不能把\n的过滤放在前面，因为还要对<和>过滤，这样就会导致<br/>失效了
        return input;
    }

    /**
     * Controller端获取searchParams
     *
     * @param request
     * @return
     */
    //public static Map<String, Object> getSearchParams(HttpServletRequest request) {
    //    Map<String, Object> searchParams = WebUtil.getParametersStartingWith(request, WebConstant.search);
    //    return searchParams;
    //}

    /**
     * Controller端获取searchParams (value not empty)
     *
     * @param request
     * @return
     */
    //public static Map<String, Object> getSearchParamsNotEmpty(HttpServletRequest request) {
    //    Map<String, Object> searchParams = getSearchParams(request);
    //    Map<String, Object> searchParamsCopy = new HashMap<String, Object>(searchParams);
    //    Set<Entry<String, Object>> entrys = searchParamsCopy.entrySet();
    //    for (Entry<String, Object> entry : entrys) {
    //        if (Util.isEmpty(entry.getValue())) {
    //            searchParams.remove(entry.getKey());
    //        }
    //    }
    //    return searchParams;
    //}

    /**
     * Controller端获取sortParams
     *
     * @param request
     * @return
     */
    //public static Map<String, Object> getSortParams(HttpServletRequest request) {
    //    Map<String, Object> sortParams = WebUtil.getParametersStartingWith(request, WebConstant.sort);
    //    return sortParams;
    //}

    /**
     * Controller端获取sortParams (value not empty)
     *
     * @param request
     * @return
     */
    //public static Map<String, Object> getSortParamsNotEmpty(HttpServletRequest request) {
    //    Map<String, Object> sortParams = getSortParams(request);
    //    Map<String, Object> sortParamsCopy = new HashMap<String, Object>(sortParams);
    //    Set<Entry<String, Object>> entrys = sortParamsCopy.entrySet();
    //    for (Entry<String, Object> entry : entrys) {
    //        if (Util.isEmpty(entry.getValue())) {
    //            sortParams.remove(entry.getKey());
    //        }
    //    }
    //    return sortParams;
    //}

    /**
     * java通用下载。如果使用spring mvc框架，请用spring mvc的专用下载。
     * 将数据写到客户端，供客户下载，比如客户端各种文件附件的下载，以及客户端图片的显示都是用这个方法输出到客户端。
     *
     * @param response
     * @param request
     * @param name     客户端附件下载的名字
     * @param b        下载数据
     * @throws IOException
     * @throws BusinessException
     * @see #write(String, byte[])
     */
    //public static final void write(HttpServletRequest request, HttpServletResponse response, String name, byte[] b)
    //        throws IOException, BusinessException {
    //    OutputStream out = null;
    //    try {
    //        out = response.getOutputStream();
    //        response.setContentType("APPLICATION/OCTET-STREAM");
    //        response.setCharacterEncoding(Constant.ENCODING);
    //        response.setHeader("Pragma", "public");
    //        response.setHeader("Cache-Control", "max-age=0");
    //        String agent = (String) request.getHeader("USER-AGENT");
    //        if (agent != null && agent.indexOf("MSIE") == -1) {
    //            // FF
    //            String enableFileName = "=?" + Constant.ENCODING + "?B?"
    //                    + (new String(Base64.encodeBase64(name.getBytes(Constant.ENCODING)))) + "?=";
    //            response.setHeader("Content-Disposition", "attachment; filename=" + enableFileName);
    //        } else {
    //            // IE
    //            String enableFileName = new String(name.getBytes("GBK"), "ISO-8859-1");
    //            response.setHeader("Content-Disposition", "attachment; filename=" + enableFileName);
    //        }
    //        out.write(b);
    //    } catch (Exception e) {
    //        throw new BusinessException(e);
    //    } finally {
    //        IOUtil.flush(out);
    //        IOUtil.close(out);
    //    }
    //}

    /**
     * 从字节数组中的start下标开始（包括start)下标，获取len个元素，返回一个新的数组。
     *
     * @param b
     * @param start
     * @param len
     * @return
     */
    public static byte[] getTargetBytes(byte[] b, int start, int len) {
        byte[] newB = new byte[len];
        int i = 0;
        if (Util.isNotEmpty(b)) {
            while (true) {
                if (i == len) {
                    break;
                }
                newB[i] = b[start];
                i++;
                start++;
            }
        }
        return newB;
    }

    /**
     * 辅助方法 数组元素 + 前缀 + 分隔符 然后拼接起来 如 参数分别是 [AA,BB,CC], TDD, -- 返回结果是
     * TDDAA--TDDBB--TDDCC
     *
     * @param deviceIds
     * @param prefix
     * @return
     */
    public static <T> String join(List<T> array, String prefix, String joinStr) {
        if (array == null || prefix == null || joinStr == null) {
            return "";
        }
        StringBuilder buff = new StringBuilder();
        for (int i = 0; i < array.size(); i++) {
            if (i == array.size() - 1) {
                buff.append(prefix + array.get(i));
            } else {
                buff.append(prefix + array.get(i) + joinStr);
            }
        }
        return buff.toString();
    }

    /**
     * 拼接sql中的in参数
     *
     * @param <T>
     * @param array
     * @param joinStr
     * @return
     */
    public static <T> String joinInStr(List<T> array) {
        if (Util.isAnyEmpty(array)) {
            return "";
        }
        StringBuilder buff = new StringBuilder();
        for (int i = 0; i < array.size(); i++) {
            if (Util.isNotEmpty(array.get(i))) {
                if (i == array.size() - 1) {
                    buff.append("'" + array.get(i) + "'");
                } else {
                    buff.append("'" + array.get(i) + "',");
                }
            }
        }
        return buff.toString();
    }

    /**
     * 辅助方法 数组元素 + 前缀 然后返回 如 参数分别是 [AA,BB,CC], TDD 返回结果是 [TDDAA,TDDBB,TDDCC]
     *
     * @param deviceIds
     * @param prefix
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> joinPrefix(List<T> array, String prefix) {
        if (array == null || prefix == null) {
            return new ArrayList<>();
        }
        List<T> newArray = new ArrayList<T>(array.size());
        for (T t : array) {
            newArray.add((T) (prefix + t));
        }
        return newArray;
    }

    /**
     * 从0度开始为正北方
     * <p>
     * Title: degrees
     * </p>
     * <p>
     * Description:根据角度转化成中文风向，16位陆地
     * </p>
     *
     * @param degrees
     * @return
     * @author dorry
     */
    public static String degrees(Double degrees) {
        if (348.75 <= degrees && degrees <= 360) {
            return "北";
        } else if (0 <= degrees && degrees <= 11.25) {
            return "北";
        } else if (11.25 < degrees && degrees <= 33.75) {
            return "东北偏北";
        } else if (33.75 < degrees && degrees <= 56.25) {
            return "东北";
        } else if (56.25 < degrees && degrees <= 78.75) {
            return "东北偏东";
        } else if (78.75 < degrees && degrees <= 101.25) {
            return "东";
        } else if (101.25 < degrees && degrees <= 123.75) {
            return "东南偏东";
        } else if (123.75 < degrees && degrees <= 146.25) {
            return "东南";
        } else if (146.25 < degrees && degrees <= 168.75) {
            return "东南偏南";
        } else if (168.75 < degrees && degrees <= 191.25) {
            return "南";
        } else if (191.25 < degrees && degrees <= 213.75) {
            return "西南偏南";
        } else if (213.75 < degrees && degrees <= 236.25) {
            return "西南";
        } else if (236.25 < degrees && degrees <= 258.75) {
            return "西南偏西";
        } else if (258.75 < degrees && degrees <= 281.25) {
            return "西";
        } else if (281.25 < degrees && degrees <= 303.75) {
            return "西北偏西";
        } else if (303.75 < degrees && degrees <= 326.25) {
            return "西北";
        } else if (326.25 < degrees && degrees < 348.75) {
            return "西北偏北";
        } else {
            return "无风";
        }
    }

    /**
     * velocity模板得到字符串，模板为绝对路径文件
     *
     * @param data
     * @param templateDir
     * @return
     */
    //public static final String getStringFromVelocityEngineByAbsuluteDir(Map<String, Object> data, String templateDir) {
    //    File templateFile = new File(templateDir);
    //    if (!templateFile.exists()) {
    //        return "";
    //    }
    //    VelocityEngine ve = new VelocityEngine();
    //    Properties p = new Properties();
    //    p.put(Velocity.FILE_RESOURCE_LOADER_PATH, templateFile.getParent());
    //    ve.init(p);
    //    Template t = ve.getTemplate(templateFile.getName(), Constant.ENCODING);
    //    return getStringFromVelocityEngine(data, t, templateFile);
    //}

    /**
     * velocity模板得到字符串，模板为相对路径文件
     *
     * @param data
     * @param templateDir
     * @return
     */
    //public static final String getStringFromVelocityEngineByRelationDir(Map<String, Object> data, String templateDir) {
    //    File templateFile = new File(templateDir);
    //    if (!templateFile.exists()) {
    //        return "";
    //    }
    //    VelocityEngine ve = new VelocityEngine();
    //    Template t = ve.getTemplate(templateFile.getPath(), Constant.ENCODING);
    //    return getStringFromVelocityEngine(data, t, templateFile);
    //}

    /**
     * velocity模板得到字符串，实现
     *
     * @param data
     * @param t
     * @param templateFile
     * @return
     */
    //private static final String getStringFromVelocityEngine(Map<String, Object> data, Template t, File templateFile) {
    //    VelocityContext context = new VelocityContext();
    //    if (!Util.isEmpty(data)) {
    //        for (Entry<String, Object> e : data.entrySet()) {
    //            context.put(e.getKey(), e.getValue());
    //        }
    //    }
    //    StringWriter writer = new StringWriter();
    //    t.merge(context, writer);
    //    log.debug(String.format("Velocity string generate for template[%s]:\n%s", templateFile.getAbsolutePath(),
    //            writer.toString()));
    //    return writer.toString();
    //}

    /**
     * 读取配置文件中的值
     * 前提是确认Application.java中读取配置文件的编码是ISO8859-1
     *
     * @param env
     * @param key
     * @return
     * @throws UnsupportedEncodingException
     */
    //public static String getProperty(Environment env, String key) throws UnsupportedEncodingException {
    //    return new String(env.getProperty(key).getBytes("ISO8859-1"), "UTF-8");
    //}

    /**
     * 过滤前端参数中的javaScript脚本和html标签，防止xss攻击
     *
     * @param value
     * @return
     */
    public static String filterDangerString(String value) {
        if (Util.isEmpty(value)) {
            return "";
        }
        value = value.replaceAll("<", "&lt;");
        value = value.replaceAll(">", "&gt;");
        return value;
    }

    /**
     * 随机生成密钥
     *
     * @return
     */
    public static String getAESRandomKey(int length) {

        ArrayList<String> strList = new ArrayList<String>();

        int begin = 97;
        // 生成小写字母,并加入集合
        for (int i = begin; i < begin + 26; i++) {
            strList.add((char) i + "");
        }
        // 生成大写字母,并加入集合
        begin = 65;
        for (int i = begin; i < begin + 26; i++) {
            strList.add((char) i + "");
        }
        // 将0-9的数字加入集合
        for (int i = 0; i < 10; i++) {
            strList.add(i + "");
        }

        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int size = strList.size();
            String randomStr = strList.get(random.nextInt(size));
            sb.append(randomStr);
        }
        return sb.toString();
    }

    /**
     * 将字符串转Integer
     *
     * @return
     */
    public static Integer getInteger(String val) {
        if (Util.isEmpty(val)) {
            return 0;
        }

        val = val.replaceAll("[^\\-0-9]", "");

        if (!isNumber(val)) {
            return 0;
        }

        try {
            return Integer.parseInt(val);
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 判断一个字符串是不是数值型字符串，包括正负小数
     *
     * @param content
     * @return
     */
    public static boolean isNumber(String content) {
        if (Util.isEmpty(content)) {
            return false;
        }

        String pattern = "^[\\+\\-]?[\\d]+(\\.[\\d|E]+)?$";
        return content.matches(pattern);
    }

    /**
     * 把输入流的内容转化成字符串
     *
     * @param is
     * @return
     * @throws IOException
     */
    public static String readInputStream(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int length = 0;
        byte[] buffer = new byte[1024];
        while ((length = is.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }
        is.close();
        baos.close();
        return baos.toString();
    }

    /**
     * hibernate返回的list map中的map是不能直接修改的，如果想修改，需要重新new一个新的list map
     *
     * @param result
     * @return
     */
    public static List<Map<String, Object>> newListMap(List<Map<String, Object>> result) {
        List<Map<String, Object>> tempList = new ArrayList<Map<String, Object>>();
        if (Util.isNotEmpty(result)) {
            for (Map<String, Object> item : result) {
                tempList.add(new HashMap<String, Object>(item));
            }
        }
        return tempList;
    }

}

