package cn.com.qmth.examcloud.core.questions.base;

import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.translate.*;
import org.dom4j.*;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * Created by songyue on 16/12/27.
 */
public final class CommonUtils {
    private static final Logger log = LoggerFactory.getLogger(CommonUtils.class);

    public static final String QUESTION_VERSION = "1.0";

    private static final String COMMON_PROPERTIES = "common.properties";

    private static final String[] CN_SMALL_NUM = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};

    private static final String[] CN_BIG_NUM = {"十", "百", "千", "万", "十万", "百万", "千万", "亿", "十亿", "百亿", "千亿"};

    public static final String PAPER_TITLE = "中国石油大学";

    public static final String PAPER_SUB_TITLE = "网络教育";

    private static Random random = new Random();

    private static final String[][] BASIC_ESCAPE = {
            {"\"", "&quot;"}, // " - double-quote
            //{"&", "&amp;"},   // & - ampersand
            {"\"", "&rdquo;"}, // < - less-than
            {"\u0192", "&fnof;"}, // latin small f with hook = function= florin, U+0192 ISOtech -->
            // <!-- Greek -->
            {"\u0391", "&Alpha;"}, // greek capital letter alpha, U+0391 -->
            {"\u0392", "&Beta;"}, // greek capital letter beta, U+0392 -->
            {"\u0393", "&Gamma;"}, // greek capital letter gamma,U+0393 ISOgrk3 -->
            {"\u0394", "&Delta;"}, // greek capital letter delta,U+0394 ISOgrk3 -->
            {"\u0395", "&Epsilon;"}, // greek capital letter epsilon, U+0395 -->
            {"\u0396", "&Zeta;"}, // greek capital letter zeta, U+0396 -->
            {"\u0397", "&Eta;"}, // greek capital letter eta, U+0397 -->
            {"\u0398", "&Theta;"}, // greek capital letter theta,U+0398 ISOgrk3 -->
            {"\u0399", "&Iota;"}, // greek capital letter iota, U+0399 -->
            {"\u039A", "&Kappa;"}, // greek capital letter kappa, U+039A -->
            {"\u039B", "&Lambda;"}, // greek capital letter lambda,U+039B ISOgrk3 -->
            {"\u039C", "&Mu;"}, // greek capital letter mu, U+039C -->
            {"\u039D", "&Nu;"}, // greek capital letter nu, U+039D -->
            {"\u039E", "&Xi;"}, // greek capital letter xi, U+039E ISOgrk3 -->
            {"\u039F", "&Omicron;"}, // greek capital letter omicron, U+039F -->
            {"\u03A0", "&Pi;"}, // greek capital letter pi, U+03A0 ISOgrk3 -->
            {"\u03A1", "&Rho;"}, // greek capital letter rho, U+03A1 -->
            // <!-- there is no Sigmaf, and no U+03A2 character either -->
            {"\u03A3", "&Sigma;"}, // greek capital letter sigma,U+03A3 ISOgrk3 -->
            {"\u03A4", "&Tau;"}, // greek capital letter tau, U+03A4 -->
            {"\u03A5", "&Upsilon;"}, // greek capital letter upsilon,U+03A5 ISOgrk3 -->
            {"\u03A6", "&Phi;"}, // greek capital letter phi,U+03A6 ISOgrk3 -->
            {"\u03A7", "&Chi;"}, // greek capital letter chi, U+03A7 -->
            {"\u03A8", "&Psi;"}, // greek capital letter psi,U+03A8 ISOgrk3 -->
            {"\u03A9", "&Omega;"}, // greek capital letter omega,U+03A9 ISOgrk3 -->
            {"\u03B1", "&alpha;"}, // greek small letter alpha,U+03B1 ISOgrk3 -->
            {"\u03B2", "&beta;"}, // greek small letter beta, U+03B2 ISOgrk3 -->
            {"\u03B3", "&gamma;"}, // greek small letter gamma,U+03B3 ISOgrk3 -->
            {"\u03B4", "&delta;"}, // greek small letter delta,U+03B4 ISOgrk3 -->
            {"\u03B5", "&epsilon;"}, // greek small letter epsilon,U+03B5 ISOgrk3 -->
            {"\u03B6", "&zeta;"}, // greek small letter zeta, U+03B6 ISOgrk3 -->
            {"\u03B7", "&eta;"}, // greek small letter eta, U+03B7 ISOgrk3 -->
            {"\u03B8", "&theta;"}, // greek small letter theta,U+03B8 ISOgrk3 -->
            {"\u03B9", "&iota;"}, // greek small letter iota, U+03B9 ISOgrk3 -->
            {"\u03BA", "&kappa;"}, // greek small letter kappa,U+03BA ISOgrk3 -->
            {"\u03BB", "&lambda;"}, // greek small letter lambda,U+03BB ISOgrk3 -->
            {"\u03BC", "&mu;"}, // greek small letter mu, U+03BC ISOgrk3 -->
            {"\u03BD", "&nu;"}, // greek small letter nu, U+03BD ISOgrk3 -->
            {"\u03BE", "&xi;"}, // greek small letter xi, U+03BE ISOgrk3 -->
            {"\u03BF", "&omicron;"}, // greek small letter omicron, U+03BF NEW -->
            {"\u03C0", "&pi;"}, // greek small letter pi, U+03C0 ISOgrk3 -->
            {"\u03C1", "&rho;"}, // greek small letter rho, U+03C1 ISOgrk3 -->
            {"\u03C2", "&sigmaf;"}, // greek small letter final sigma,U+03C2 ISOgrk3 -->
            {"\u03C3", "&sigma;"}, // greek small letter sigma,U+03C3 ISOgrk3 -->
            {"\u03C4", "&tau;"}, // greek small letter tau, U+03C4 ISOgrk3 -->
            {"\u03C5", "&upsilon;"}, // greek small letter upsilon,U+03C5 ISOgrk3 -->
            {"\u03C6", "&phi;"}, // greek small letter phi, U+03C6 ISOgrk3 -->
            {"\u03C7", "&chi;"}, // greek small letter chi, U+03C7 ISOgrk3 -->
            {"\u03C8", "&psi;"}, // greek small letter psi, U+03C8 ISOgrk3 -->
            {"\u03C9", "&omega;"}, // greek small letter omega,U+03C9 ISOgrk3 -->
            {"\u03D1", "&thetasym;"}, // greek small letter theta symbol,U+03D1 NEW -->
            {"\u03D2", "&upsih;"}, // greek upsilon with hook symbol,U+03D2 NEW -->
            {"\u03D6", "&piv;"}, // greek pi symbol, U+03D6 ISOgrk3 -->
            // <!-- General Punctuation -->
            {"\u2022", "&bull;"}, // bullet = black small circle,U+2022 ISOpub -->
            // <!-- bullet is NOT the same as bullet operator, U+2219 -->
            {"\u2026", "&hellip;"}, // horizontal ellipsis = three dot leader,U+2026 ISOpub -->
            {"\u2032", "&prime;"}, // prime = minutes = feet, U+2032 ISOtech -->
            {"\u2033", "&Prime;"}, // double prime = seconds = inches,U+2033 ISOtech -->
            {"\u203E", "&oline;"}, // overline = spacing overscore,U+203E NEW -->
            {"\u2044", "&frasl;"}, // fraction slash, U+2044 NEW -->
            // <!-- Letterlike Symbols -->
            {"\u2118", "&weierp;"}, // script capital P = power set= Weierstrass p, U+2118 ISOamso -->
            {"\u2111", "&image;"}, // blackletter capital I = imaginary part,U+2111 ISOamso -->
            {"\u211C", "&real;"}, // blackletter capital R = real part symbol,U+211C ISOamso -->
            {"\u2122", "&trade;"}, // trade mark sign, U+2122 ISOnum -->
            {"\u2135", "&alefsym;"}, // alef symbol = first transfinite cardinal,U+2135 NEW -->
            // <!-- alef symbol is NOT the same as hebrew letter alef,U+05D0 although the
            // same glyph could be used to depict both characters -->
            // <!-- Arrows -->
            {"\u2190", "&larr;"}, // leftwards arrow, U+2190 ISOnum -->
            {"\u2191", "&uarr;"}, // upwards arrow, U+2191 ISOnum-->
            {"\u2192", "&rarr;"}, // rightwards arrow, U+2192 ISOnum -->
            {"\u2193", "&darr;"}, // downwards arrow, U+2193 ISOnum -->
            {"\u2194", "&harr;"}, // left right arrow, U+2194 ISOamsa -->
            {"\u21B5", "&crarr;"}, // downwards arrow with corner leftwards= carriage return, U+21B5 NEW -->
            {"\u21D0", "&lArr;"}, // leftwards double arrow, U+21D0 ISOtech -->
            // <!-- ISO 10646 does not say that lArr is the same as the 'is implied by'
            // arrow but also does not have any other character for that function.
            // So ? lArr canbe used for 'is implied by' as ISOtech suggests -->
            {"\u21D1", "&uArr;"}, // upwards double arrow, U+21D1 ISOamsa -->
            {"\u21D2", "&rArr;"}, // rightwards double arrow,U+21D2 ISOtech -->
            // <!-- ISO 10646 does not say this is the 'implies' character but does not
            // have another character with this function so ?rArr can be used for
            // 'implies' as ISOtech suggests -->
            {"\u21D3", "&dArr;"}, // downwards double arrow, U+21D3 ISOamsa -->
            {"\u21D4", "&hArr;"}, // left right double arrow,U+21D4 ISOamsa -->
            // <!-- Mathematical Operators -->
            {"\u2200", "&forall;"}, // for all, U+2200 ISOtech -->
            {"\u2202", "&part;"}, // partial differential, U+2202 ISOtech -->
            {"\u2203", "&exist;"}, // there exists, U+2203 ISOtech -->
            {"\u2205", "&empty;"}, // empty set = null set = diameter,U+2205 ISOamso -->
            {"\u2207", "&nabla;"}, // nabla = backward difference,U+2207 ISOtech -->
            {"\u2208", "&isin;"}, // element of, U+2208 ISOtech -->
            {"\u2209", "&notin;"}, // not an element of, U+2209 ISOtech -->
            {"\u220B", "&ni;"}, // contains as member, U+220B ISOtech -->
            // <!-- should there be a more memorable name than 'ni'? -->
            {"\u220F", "&prod;"}, // n-ary product = product sign,U+220F ISOamsb -->
            // <!-- prod is NOT the same character as U+03A0 'greek capital letter pi'
            // though the same glyph might be used for both -->
            {"\u2211", "&sum;"}, // n-ary summation, U+2211 ISOamsb -->
            // <!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
            // though the same glyph might be used for both -->
            {"\u2212", "&minus;"}, // minus sign, U+2212 ISOtech -->
            {"\u2217", "&lowast;"}, // asterisk operator, U+2217 ISOtech -->
            {"\u221A", "&radic;"}, // square root = radical sign,U+221A ISOtech -->
            {"\u221D", "&prop;"}, // proportional to, U+221D ISOtech -->
            {"\u221E", "&infin;"}, // infinity, U+221E ISOtech -->
            {"\u2220", "&ang;"}, // angle, U+2220 ISOamso -->
            {"\u2227", "&and;"}, // logical and = wedge, U+2227 ISOtech -->
            {"\u2228", "&or;"}, // logical or = vee, U+2228 ISOtech -->
            {"\u2229", "&cap;"}, // intersection = cap, U+2229 ISOtech -->
            {"\u222A", "&cup;"}, // union = cup, U+222A ISOtech -->
            {"\u222B", "&int;"}, // integral, U+222B ISOtech -->
            {"\u2234", "&there4;"}, // therefore, U+2234 ISOtech -->
            {"\u223C", "&sim;"}, // tilde operator = varies with = similar to,U+223C ISOtech -->
            // <!-- tilde operator is NOT the same character as the tilde, U+007E,although
            // the same glyph might be used to represent both -->
            {"\u2245", "&cong;"}, // approximately equal to, U+2245 ISOtech -->
            {"\u2248", "&asymp;"}, // almost equal to = asymptotic to,U+2248 ISOamsr -->
            {"\u2260", "&ne;"}, // not equal to, U+2260 ISOtech -->
            {"\u2261", "&equiv;"}, // identical to, U+2261 ISOtech -->
            {"\u2264", "&le;"}, // less-than or equal to, U+2264 ISOtech -->
            {"\u2265", "&ge;"}, // greater-than or equal to,U+2265 ISOtech -->
            {"\u2282", "&sub;"}, // subset of, U+2282 ISOtech -->
            {"\u2283", "&sup;"}, // superset of, U+2283 ISOtech -->
            // <!-- note that nsup, 'not a superset of, U+2283' is not covered by the
            // Symbol font encoding and is not included. Should it be, for symmetry?
            // It is in ISOamsn -->,
            {"\u2284", "&nsub;"}, // not a subset of, U+2284 ISOamsn -->
            {"\u2286", "&sube;"}, // subset of or equal to, U+2286 ISOtech -->
            {"\u2287", "&supe;"}, // superset of or equal to,U+2287 ISOtech -->
            {"\u2295", "&oplus;"}, // circled plus = direct sum,U+2295 ISOamsb -->
            {"\u2297", "&otimes;"}, // circled times = vector product,U+2297 ISOamsb -->
            {"\u22A5", "&perp;"}, // up tack = orthogonal to = perpendicular,U+22A5 ISOtech -->
            {"\u22C5", "&sdot;"}, // dot operator, U+22C5 ISOamsb -->
            // <!-- dot operator is NOT the same character as U+00B7 middle dot -->
            // <!-- Miscellaneous Technical -->
            {"\u2308", "&lceil;"}, // left ceiling = apl upstile,U+2308 ISOamsc -->
            {"\u2309", "&rceil;"}, // right ceiling, U+2309 ISOamsc -->
            {"\u230A", "&lfloor;"}, // left floor = apl downstile,U+230A ISOamsc -->
            {"\u230B", "&rfloor;"}, // right floor, U+230B ISOamsc -->
            {"\u2329", "&lang;"}, // left-pointing angle bracket = bra,U+2329 ISOtech -->
            // <!-- lang is NOT the same character as U+003C 'less than' or U+2039 'single left-pointing angle quotation
            // mark' -->
            {"\u232A", "&rang;"}, // right-pointing angle bracket = ket,U+232A ISOtech -->
            // <!-- rang is NOT the same character as U+003E 'greater than' or U+203A
            // 'single right-pointing angle quotation mark' -->
            // <!-- Geometric Shapes -->
            {"\u25CA", "&loz;"}, // lozenge, U+25CA ISOpub -->
            // <!-- Miscellaneous Symbols -->
            {"\u2660", "&spades;"}, // black spade suit, U+2660 ISOpub -->
            // <!-- black here seems to mean filled as opposed to hollow -->
            {"\u2663", "&clubs;"}, // black club suit = shamrock,U+2663 ISOpub -->
            {"\u2665", "&hearts;"}, // black heart suit = valentine,U+2665 ISOpub -->
            {"\u2666", "&diams;"}, // black diamond suit, U+2666 ISOpub -->

            // <!-- Latin Extended-A -->
            {"\u0152", "&OElig;"}, // -- latin capital ligature OE,U+0152 ISOlat2 -->
            {"\u0153", "&oelig;"}, // -- latin small ligature oe, U+0153 ISOlat2 -->
            // <!-- ligature is a misnomer, this is a separate character in some languages -->
            {"\u0160", "&Scaron;"}, // -- latin capital letter S with caron,U+0160 ISOlat2 -->
            {"\u0161", "&scaron;"}, // -- latin small letter s with caron,U+0161 ISOlat2 -->
            {"\u0178", "&Yuml;"}, // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 -->
            // <!-- Spacing Modifier Letters -->
            {"\u02C6", "&circ;"}, // -- modifier letter circumflex accent,U+02C6 ISOpub -->
            {"\u02DC", "&tilde;"}, // small tilde, U+02DC ISOdia -->
            // <!-- General Punctuation -->
            {"\u2002", "&ensp;"}, // en space, U+2002 ISOpub -->
            {"\u2003", "&emsp;"}, // em space, U+2003 ISOpub -->
            {"\u2009", "&thinsp;"}, // thin space, U+2009 ISOpub -->
            {"\u200C", "&zwnj;"}, // zero width non-joiner,U+200C NEW RFC 2070 -->
            {"\u200D", "&zwj;"}, // zero width joiner, U+200D NEW RFC 2070 -->
            {"\u200E", "&lrm;"}, // left-to-right mark, U+200E NEW RFC 2070 -->
            {"\u200F", "&rlm;"}, // right-to-left mark, U+200F NEW RFC 2070 -->
            {"\u2013", "&ndash;"}, // en dash, U+2013 ISOpub -->
            {"\u2014", "&mdash;"}, // em dash, U+2014 ISOpub -->
            {"\u2018", "&lsquo;"}, // left single quotation mark,U+2018 ISOnum -->
            {"\u2019", "&rsquo;"}, // right single quotation mark,U+2019 ISOnum -->
            {"\u201A", "&sbquo;"}, // single low-9 quotation mark, U+201A NEW -->
            {"\u201C", "&ldquo;"}, // left double quotation mark,U+201C ISOnum -->
            //{"\u201D", "&rdquo;"}, // right double quotation mark,U+201D ISOnum -->
            {"\u201E", "&bdquo;"}, // double low-9 quotation mark, U+201E NEW -->
            {"\u2020", "&dagger;"}, // dagger, U+2020 ISOpub -->
            {"\u2021", "&Dagger;"}, // double dagger, U+2021 ISOpub -->
            {"\u2030", "&permil;"}, // per mille sign, U+2030 ISOtech -->
            {"\u2039", "&lsaquo;"}, // single left-pointing angle quotation mark,U+2039 ISO proposed -->
            // <!-- lsaquo is proposed but not yet ISO standardized -->
            {"\u203A", "&rsaquo;"}, // single right-pointing angle quotation mark,U+203A ISO proposed -->
            // <!-- rsaquo is proposed but not yet ISO standardized -->
            {"\u20AC", "&euro;"}, // -- euro sign, U+20AC NEW -->
    };

    private static final String[][] BASIC_UNESCAPE = invert(BASIC_ESCAPE);

    /**
     * 加载properties配置文件
     *
     * @param propertiesPath
     * @return
     */
    public static Properties loadProperties(String propertiesPath) {
        InputStream inputStream = null;
        Properties properties = new Properties();
        try {
            inputStream = ClassLoader.getSystemResourceAsStream(propertiesPath);
            BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream));
            properties.load(bf);
        } catch (Exception e) {
            log.error("加载配置异常:", e.getMessage());
        }
        return properties;
    }

    /**
     * 记载通用配置文件
     *
     * @return
     */
    public static Properties loadCommonProperties() {
        return loadProperties(COMMON_PROPERTIES);
    }

    /**
     * 获取临时文件夹根路径
     *
     * @return
     */
    public static String getTmpRootPath() {
        return (String) loadCommonProperties().get("tmp_root_path");
    }

    /**
     * 获取当前日期字符串
     *
     * @return
     */
    public static String getCurDate() {
        return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    }

    /**
     * 获取当前日期时间字符串
     *
     * @return
     */
    public static String getCurDateTime() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }

    /**
     * 根据当前日期获取数字字符串
     *
     * @return
     */
    public static String getCurNum() {
        return new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
    }

    /**
     * 根据枚举名称获取枚举
     *
     * @param clazz
     * @param name
     * @param <T>
     * @return
     */
    public static <T> T getEnum(Class<T> clazz, String name) {
        T[] enumConstants = clazz.getEnumConstants();
        for (T _enum : enumConstants) {
            if (_enum.toString().equals(name)) {
                return _enum;
            }
        }
        return null;
    }

    /**
     * 校验题型
     *
     * @param quesType
     * @return
     */
    public static boolean checkQuesType(String quesType) {
        return Stream.of(QuesStructType.values()).anyMatch(type -> type.getName().equals(quesType));
    }

    /**
     * 获取中文数字
     *
     * @param inputNum
     * @return
     */
    public static String toCHNum(int inputNum) {
        String resultNum = "";
        String numStr = String.valueOf(inputNum);

        int len = numStr.length();
        for (int i = 0; i < len; i++) {
            int tmpNum = numStr.charAt(i) - '0';
            if (i != len - 1 && tmpNum != 0) {
                resultNum += CN_SMALL_NUM[tmpNum] + CN_BIG_NUM[len - 2 - i];
            } else {
                resultNum += CN_SMALL_NUM[tmpNum];
            }
        }

        if (resultNum.contains("零")) {
            resultNum = resultNum.replaceAll("零{1,}", "零");
        }
        if (resultNum.endsWith("零")) {
            resultNum = resultNum.substring(0, resultNum.length() - 1);
        }
        if (resultNum.startsWith("一十")) {
            resultNum = resultNum.replaceFirst("一十", "十");
        }

        while (StringUtils.countMatches(resultNum, "万") > 1) {
            resultNum = resultNum.replaceFirst("万", "");
        }

        while (StringUtils.countMatches(resultNum, "亿") > 1) {
            resultNum = resultNum.replaceFirst("亿", "");
        }

        return resultNum;
    }

    /**
     * iterator转List
     *
     * @param <T>
     * @param iterator
     * @return
     */
    public static <T> List<T> toList(Iterable<T> iterator) {
        if (iterator == null) {
            throw new NullPointerException("Iterator must not be null");
        }
        final List<T> list = new ArrayList<T>();
        final Iterator<T> iter = iterator.iterator();
        while (iter.hasNext())
            list.add(iter.next());
        return list;
    }

    /**
     * 将整数转化成字母
     *
     * @param number
     * @return
     */
    public static String getOptionNum(int number) {
        char optionNum = (char) (65 + number);
        return String.valueOf(optionNum);
    }

    /**
     * 字母转换成数字
     *
     * @param input
     * @return
     */
    public static Integer characterToNumber(String input) {
        String reg = "[a-zA-Z]";
        StringBuffer strBuf = new StringBuffer();
        input = input.toLowerCase();
        if (null != input && !"".equals(input)) {
            for (char c : input.toCharArray()) {
                if (String.valueOf(c).matches(reg)) {
                    strBuf.append(c - 96);
                } else {
                    strBuf.append(c);
                }
            }
            return Integer.valueOf(strBuf.toString());
        } else {
            return null;
        }
    }

    /**
     * 从A标签中获取属性值
     * 例如从:
     * <a id=\"123456\" name=\"1_1_1.mp3\">
     * 中获取id的属性值为:123456
     * 获取name的属性值为:1_1_1.mp3
     *
     * @param questionStr
     * @param attrName
     * @return
     */
    public static String getAttrValue(String questionStr, String attrName) {
        Pattern aPattern = Pattern.compile("a.*");
        Matcher aMatcher = aPattern.matcher(questionStr);

        if (aMatcher.find()) {
            String idRegex = attrName + "=\".*?\"";
            Pattern idPattern = Pattern.compile(idRegex);
            Matcher idMatcher = idPattern.matcher(aMatcher.group());
            if (idMatcher.find()) {
                return idMatcher.group()
                        .replaceAll(attrName + "=\"", "")
                        .replaceAll("\"", "");
            }
        }

        return "";
    }

    /**
     * 从一段HTML字符串中取出标签中的属性值
     *
     * @param htmlString
     * @return
     * @throws DocumentException
     */
    public static List<String> getAttrValueFromString(String htmlString, String flagName, String attrName) {
        List<String> idValues = new ArrayList<>();
        try {
            Document document = DocumentHelper.parseText(htmlString);
            Element rootElement = document.getRootElement();
            List<Element> nodes = new ArrayList<>();
            if (flagName.equals(rootElement.getName())) {
                nodes.add(rootElement);
            } else {
                nodes = rootElement.elements(flagName);
            }
            for (Iterator<Element> itr = nodes.iterator(); itr.hasNext(); ) {
                Element element = itr.next();
                Attribute attr = element.attribute(attrName);
                idValues.add(attr.getValue());
            }
        } catch (DocumentException e) {
            log.error(e.getMessage(), e);
        }
        return idValues;
    }

    /**
     * 截取a标签中的id的值
     *
     * @param htmlString
     * @return
     */
    public static List<String> getTagANames(String htmlString) {
        List<String> list = new ArrayList<>();
        Pattern p = Pattern.compile("<a[^<>]*\\s+id=\"([0-9A-Za-z-_.]+)\"\\s*");
        Matcher m = p.matcher(htmlString);
        while (m.find()) {
            String str = m.group(1);
            list.add(str);
        }
        return list;
    }

    /**
     * 截取a标签中的id的值
     *
     * @param htmlString
     * @return
     */
    public static List<String> getTagANames2(String htmlString) {
        List<String> list = new ArrayList<>();
        Pattern p = Pattern.compile("<a[^<>]*\\s+name=\"([0-9A-Za-z-_.]+)\"\\s*");
        Matcher m = p.matcher(htmlString);
        while (m.find()) {
            String str = m.group(1);
            list.add(str);
        }
        return list;
    }

    /**
     * 保留两位小数
     *
     * @param number
     * @return
     */
    public static double formatDouble(double number) {
        BigDecimal formatNumber = BigDecimal.valueOf(number);
        return formatNumber.setScale(2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 保留一位小数点
     */
    public static String formatValue(Double score) {
        if (score == null) {
            return "";
        }
        DecimalFormat decimalFormat = new DecimalFormat("#.#");
        return decimalFormat.format(score);
    }

    /**
     * 去掉包含“.0”的小数点
     */
    public static String clearZeroPoint(String scoreStr) {
        if (scoreStr != null && scoreStr.indexOf(".0") > -1) {
            return scoreStr.replace(".0", "");
        }
        return scoreStr;
    }

    /**
     * 向下取整,可以取0.5
     *
     * @param number
     * @return
     */
    public static double formatDoubleFloor(double number) {
        BigDecimal formatNumber = BigDecimal.valueOf(number);
        double floorNumber = formatNumber.setScale(0, RoundingMode.FLOOR).doubleValue();
        if (number >= floorNumber && number < floorNumber + 0.5) {
            return floorNumber;
        } else {
            return floorNumber + 0.5;
        }
    }

    /**
     * 补全html标签
     *
     * @param htmlStr
     * @return
     * @throws Exception
     */
    public static String repairHtmlStr(String htmlStr) throws Exception {
        htmlStr = htmlStr.trim();
        if (htmlStr.toLowerCase().contains("<!doctype html ")) {
            int index1 = htmlStr.toLowerCase().indexOf("<!doctype html ");
            int index2 = htmlStr.indexOf('>', index1 + 1);
            htmlStr = htmlStr.substring(0, index1) + htmlStr.substring(index2 + 1);
        }
        while (htmlStr.toLowerCase().contains("<br ")) {
            int index1 = htmlStr.toLowerCase().indexOf("<br ");
            int index2 = htmlStr.toLowerCase().indexOf('>', index1 + 1);
            htmlStr = htmlStr.substring(0, index1) + "<br/>" + htmlStr.substring(index2 + 1);
        }
        while (htmlStr.toLowerCase().endsWith("<br>") || htmlStr.toLowerCase().endsWith("<br/>")) {
            if (htmlStr.toLowerCase().endsWith("<br>")) {
                htmlStr = htmlStr.substring(0, htmlStr.length() - "<br>".length());
            } else if (htmlStr.toLowerCase().endsWith("<br/>")) {
                htmlStr = htmlStr.substring(0, htmlStr.length() - "<br/>".length());
            }
        }
        htmlStr = htmlStr.replace("<br>", "<br/>").replace("<BR>", "<br/>");

        {//补全META标签
            int imgIndex = indexOfRegex(htmlStr, "<((meta)|(META)) ");
            while (imgIndex > 0) {
                int flag = htmlStr.indexOf('>', imgIndex);
                if (htmlStr.charAt(flag - 1) != '/') {
                    htmlStr = htmlStr.substring(0, flag) + "/" + htmlStr.substring(flag);
                }
                imgIndex = indexOfRegex(htmlStr, "<((meta)|(META)) ", flag);
            }
        }

        {//补全img标签
            int imgIndex = indexOfRegex(htmlStr, "<((img)|(IMG)) ");
            while (imgIndex > 0) {
                int flag = htmlStr.indexOf('>', imgIndex);
                if (htmlStr.charAt(flag - 1) != '/') {
                    htmlStr = htmlStr.substring(0, flag) + "/" + htmlStr.substring(flag);
                }
                imgIndex = indexOfRegex(htmlStr, "<((img)|(IMG)) ", flag);
            }
        }
        //添加body标签
        if ((htmlStr.toLowerCase().contains("<p") || htmlStr.toLowerCase().contains("<span"))
                && !htmlStr.toLowerCase().contains("<body")) {
            htmlStr = "<body>" + htmlStr + "</body>";
        }
        return new String(htmlStr.getBytes("UTF-8"));
    }

    /**
     * 从指定的位置开始查找第一个匹配正则表达式的字符串的位置
     *
     * @param str
     * @param regex     正则表达式
     * @param fromIndex 指定的起始位置
     * @return
     */
    public static int indexOfRegex(String str, String regex, int fromIndex) {
        int index = indexOfRegex(str.substring(fromIndex), regex);
        if (index < 0) {
            return -1;
        }
        return fromIndex + index;
    }

    /**
     * 查找第一个匹配正则表达式的字符串的位置
     *
     * @param str
     * @param regex 正则表达式
     * @return
     */
    public static int indexOfRegex(String str, String regex) {
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(str);
        if (m.find()) {
            return m.start();
        } else {
            return -1;
        }
    }

    /**
     * 格式化html
     *
     * @param htmlStr
     * @return
     * @throws Exception
     */
    public static String formatHtml(String htmlStr) throws Exception {
        if (StringUtils.isEmpty(htmlStr)) {
            return "";
        }
        htmlStr = repairHtmlStr(htmlStr);
        //htmlStr = StringEscapeUtils.unescapeHtml4(htmlStr);
        htmlStr = UNESCAPE_HTML4.translate(htmlStr);
        return htmlStr;
    }

    /**
     * 过滤非空格的空字符
     *
     * @param str
     * @return
     */
    public static String trimNoBlankSpace(final String str) {
        if (str == null || str.length() == 0) {
            return "";
        }
        return str.replaceAll("[\\t\\r\\f]*", "");
    }

    /**
     * 替换掉字符串的Unicode字符为中文字符
     * 如下所示
     * \u0026ldquo;=“
     * \u0026mdash;=—
     * \u0026lt;=<
     * \u0026hellip;=…
     * \u0026rdquo;=”
     * \u0026gt;=>
     */
    public static String replaceUnicodeStr(String content) {
        try {
            Pattern pattern = Pattern.compile("\\\\u[a-f0-9A-F]{1,4}");
            Matcher matcher = pattern.matcher(content);
            Map<String, String> unicodeMap = new HashMap<>();
            while (matcher.find()) {
                String unicodeStr = matcher.group();
                byte[] unicodeStrByte = unicodeStr.getBytes();
                String resultByte = new String(unicodeStrByte, "utf-8");
                String escapeStr = StringEscapeUtils.unescapeHtml4(StringEscapeUtils.unescapeEcmaScript(resultByte));
                unicodeMap.put(unicodeStr, escapeStr);
            }

            if (unicodeMap.size() > 0) {
                for (String key : unicodeMap.keySet()) {
                    //replace函数替换时忽略正则表达式符号，replaceAll和replaceFirst函数替换时是使用正则表达式匹配的。
                    content = content.replace(key, unicodeMap.get(key));
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return content;
    }

    /**
     * weiwenhai
     * 过滤题干中<>,不让反转
     */
    public static final CharSequenceTranslator UNESCAPE_HTML4 =
            new AggregateTranslator(
                    new LookupTranslator(BASIC_UNESCAPE),
                    new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
                    new NumericEntityUnescaper()
            );

    public static String[][] invert(final String[][] array) {
        final String[][] newarray = new String[array.length][2];
        for (int i = 0; i < array.length; i++) {
            newarray[i][0] = array[i][1];
            newarray[i][1] = array[i][0];
        }
        return newarray;
    }

    /**
     * 删除html中p标签
     *
     * @param htmlStr
     * @return
     */
    public static String deleteHtmlP(String htmlStr) {
        htmlStr = htmlStr.replace("<p>", "").replace("</p>", "");
        return htmlStr;
    }

    /**
     * 生成不同的随机整数集合
     *
     * @param list
     * @param size     数量
     * @param original 随机数范围
     * @return
     */
    public static List<Integer> getRandom(List<Integer> list, int size, int original) {
        Integer i = random.nextInt(original);
        if (!list.contains(i) && i < original) {
            list.add(i);
            if (list.size() == size) {
                return list;
            }
        }
        getRandom(list, size, original);
        return list;
    }

    /**
     * 判断字符串是否为正数
     *
     * @param str
     * @return
     */
    public static boolean isInteger(String str) {
        Boolean strResult = str.matches("-?[0-9]+.?[0-9]*");
        try {
            if (strResult && Double.parseDouble(str) > 0) {
                return true;
            }
        } catch (NumberFormatException ex) {
            return false;
        }
        return false;
    }

    public static String relaceQuestionIdx(String str, int baseIdx) {
        StringBuffer sb = new StringBuffer("");
        Pattern pattern = Pattern.compile("##(\\d+)##");

        Matcher matcher = pattern.matcher(str);

        while (matcher.find()) {
            String idx = matcher.group(1);
            matcher.appendReplacement(sb, "___" + String.valueOf(Integer.parseInt(idx) + baseIdx) + "___");
        }

        if (StringUtils.isEmpty(sb.toString())) {
            return str;
        } else {
            matcher.appendTail(sb);
            return sb.toString();
        }
    }

    /**
     * 去除字符串中所有的html标签
     *
     * @param html
     * @return
     */
    public static String parseHtml(String html) {
        if (StringUtils.isEmpty(html)) {
            return "";
        }
        org.jsoup.nodes.Document document = Jsoup.parse(html);
        return document.text();
    }

    /**
     * 正则表达是匹配特殊字符进行转义
     *
     * @param keyword
     * @return
     */
    public static String escapeExprSpecialWord(String keyword) {
        if (StringUtils.isNotBlank(keyword)) {
            String[] fbsArr = {"\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|"};
            for (String key : fbsArr) {
                if (keyword.contains(key)) {
                    keyword = keyword.replaceAll("\\" +key, "\\\\" + key);
                }
            }
        }
        return keyword;
    }
}
