package com.qmth.qrzk.repository.utils;

import com.qmth.qrzk.repository.user.model.Question;
import com.qmth.qrzk.repository.user.model.QuestionOption;
import com.qmth.qrzk.repository.utils.docx.DefaultContentType;
import com.qmth.qrzk.repository.utils.docx.DocxReader;
import com.qmth.qrzk.repository.utils.docx.HtmlCreator;
import com.qmth.qrzk.repository.utils.docx.ResourceInfo;
import net.sf.saxon.TransformerFactoryImpl;
import net.sourceforge.jeuclid.LayoutContext;
import net.sourceforge.jeuclid.context.LayoutContextImpl;
import net.sourceforge.jeuclid.context.Parameter;
import net.sourceforge.jeuclid.context.StyleAttributeLayoutContext;
import net.sourceforge.jeuclid.converter.Converter;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.RFonts;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.htmlparser.util.ParserException;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import org.xml.sax.InputSource;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.awt.*;
import java.io.*;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 
 * ClassName：General Description:主要工具类 包含大部分常用函数 author：chengsonglin
 * createdate：2014-2-11
 * 
 * @version
 * 
 */
public class General {
	
	private static Logger log = Logger.getLogger(General.class);
	
	private static void initDir(String dirPath){
		try {
			File dir = new File(dirPath);
			if (dir.exists() && !dir.isDirectory()) {
				throw new Exception(dir.getPath() + "不是文件夹！");
			}
			if (!dir.exists()) {
				dir.mkdirs();
			}
		} catch (Exception e) {
			System.err.println("无法使用系统配置的文件夹【" + dirPath + "】，原因：" + e.getMessage());
			e.printStackTrace();
			throw new RuntimeException("无法使用系统配置的文件夹【" + dirPath + "】，原因：" + e.getMessage(), e);
		}
	}
	
	/**
	 * 路径的后面自动补充 “/”
	 * @param path
	 * @return
	 */
	public static String fixPathStr(String path){
		char lastChar = path.charAt(path.length() - 1);
		if (lastChar != '/' && lastChar != '\\') {
			path = path + "/";
		}
		return path;
	}

	/**
	 * 初始化系统配置的文件夹路径，系统加载时会判断此路径是否存在，如果不存在，自动创建之。同时对ParamUtils.TemporaryDirPath等路径的后面自动补充 “/”
	 * 
	 * @author 马涛 2014-09-29
	 */
	public static void initSysDir() {
		initDir(ParamUtils.LuceneIndexURL);
		initDir(ParamUtils.TemporaryDirPath);
		initDir(ParamUtils.AttDirPath);
		ParamUtils.LuceneIndexURL = fixPathStr(ParamUtils.LuceneIndexURL);
		ParamUtils.TemporaryDirPath = fixPathStr(ParamUtils.TemporaryDirPath);
		ParamUtils.AttDirPath = fixPathStr(ParamUtils.AttDirPath);
		//TODO 清空临时文件夹里面一天前的临时文件
	}

	/**
	 * 判断字符串是否为空
	 * 
	 * @param str
	 * @return
	 */
	public static boolean isEmpty(String str) {
		return str == null || "".equals(str);
	}

	/**
	 * 判断字符串是否不为空
	 * 
	 * @param str
	 * @return
	 */
	public static boolean isNotEmpty(String str) {
		return str != null && !("".equals(str));
	}

	/**
	 * 判断是否为数字
	 * 
	 * @param str
	 * @return
	 */
	public static boolean isNumber(String str) {
		try {
			Integer.parseInt(str);
			return true;
		} catch (Exception ex) {
			return false;
		}
	}

	/**
	 * 将null转换为空字符串
	 * 
	 * @param str
	 * @return
	 */
	public static String convertNullToEmpty(String str) {
		return str == null ? "" : str;
	}

	/**
	 * HTML字符串编码
	 * 
	 * @param str
	 * @param ignoreEnter 是否忽略回车
	 * @return
	 */
	public static String HTMLEncode(String str,boolean ignoreEnter) {
		if (str != null) {
			str = str.replace("&", "&amp;");
			for(Entry<String, String> entry:Consts.HtmlSymbolWholeMap.entrySet()){
				if(!"&amp;".equals(entry.getKey()) && !"&shy;".equals(entry.getKey())){
					str = str.replace(entry.getValue(), entry.getKey());
				}
			}
			if(!ignoreEnter){
				str = str.replace("\r\n", "<br/>")
					.replace("\n", "<br/>");
			}
		}
		return str;
	}

	/**
	 * XML字符串编码，把转义字符转义成对应的字符串
	 * 
	 * @param str
	 * @return
	 */
	public static String XMLEncode(String str) {
		if (str != null) {
			return str.replace("&", "&amp;").replace("<", "&lt;")
					.replace(">", "&gt;").replace("’", "&apos;")
					.replace("\"", "&quot;");
		} else {
			return "";
		}
	}

	/**
	 * HTML字符串解码
	 * 
	 * @param str
	 * @return
	 */
	public static String HTMLDecode(String str) {
		if (str != null) {
			str = str.replace("<br>", "\r\n")
					.replace("<br/>", "\r\n");
			for(Entry<String, String> entry:Consts.HtmlSymbolWholeMap.entrySet()){
				str = str.replace(entry.getKey(), entry.getValue());
			}
		}
		return str;
	}

	/**
	 * 防止SQL注入
	 * 
	 * @param str
	 * @return
	 */
	public static String sqlStr(String str) {
		if (str != null) {
			str = str.replace("'", "")
					.replace(" ", "")
					.replace("\\", "&#92;");
		}
		return str;
	}
	
	/**
	 * 提取Html里面的文本信息，并把一般的转义字符串转换回来
	 * @param inputHtml
	 * @return
	 * @throws ParserException
	public static String extractText1(String inputHtml) throws ParserException{
	    StringBuffer text = new StringBuffer();
	    Parser parser = Parser.createParser(inputHtml,"UTF-8");
	    // 遍历所有的节点
//	    NodeList nodes = parser.extractAllNodesThatMatch(new NodeFilter() {
//			private static final long serialVersionUID = -6849115382332238554L;
//			public boolean accept(Node node) {
//	          return true;
//	        }
//	    });
	    NodeList nodes = parser.extractAllNodesThatMatch(new NodeFilter() {
			private static final long serialVersionUID = -6849115382332238554L;
			public boolean accept(Node node) {
				boolean hasIgnoreNode = false;
				if(node instanceof DoctypeTag || node instanceof Html){
					return false;
				}
				Node pNode = node;
				while(pNode != null){
					if(pNode instanceof HeadTag || pNode instanceof ScriptTag || pNode instanceof StyleTag){
						hasIgnoreNode = true;
						break;
					}
					pNode = pNode.getParent();
				}
				if(!hasIgnoreNode){
//					System.out.println(node.getText());
					return true;
				}else{
					return false;
				}
	        }
		});
	 
	    for (int i=0;i<nodes.size();i++){
	    	Node nodet = nodes.elementAt(i);
	    	System.out.println(nodet.getClass() + ":" + (nodet.getParent() == null?"null":nodet.getParent().getClass()) + ":" + nodet.toPlainTextString());
	        text.append(nodet.toPlainTextString()).append("\r\n");
	    }
	    return HTMLDecode(text.toString().trim());
	 }
	 */
	
	/**
	 * 提取Html里面的文本信息，并把一般的转义字符串转换回来
	 * @param htmlStr
	 * @return
	 * @throws ParserException
	 */
	public static String extractText(String htmlStr){
		if(isEmpty(htmlStr)){
			return "";
		}
		if(htmlStr.toLowerCase().contains("<!doctype")){
			int index1 = htmlStr.toLowerCase().indexOf("<!doctype");
			int index2 = htmlStr.indexOf('>',index1 + 1);
			htmlStr = htmlStr.substring(0, index1) + htmlStr.substring(index2 + 1);
		}
		String regEx_head = "<[\\s]*?head[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?head[\\s]*?>"; //定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
		String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>"; //定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
        String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>"; //定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
        String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式

        Pattern p_head = Pattern.compile(regEx_head,Pattern.CASE_INSENSITIVE);
        Matcher m_head = p_head.matcher(htmlStr);
        htmlStr = m_head.replaceAll(""); //过滤script标签

        Pattern p_script = Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
        Matcher m_script = p_script.matcher(htmlStr);
        htmlStr = m_script.replaceAll(""); //过滤script标签

        Pattern p_style = Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE);
        Matcher m_style = p_style.matcher(htmlStr);
        htmlStr = m_style.replaceAll(""); //过滤style标签

        htmlStr = htmlStr.replace("<div", " <div").replace("<DIV", " <DIV")
        		.replace("<p", " <p").replace("<P", " <P")
        		.replace("<h", " <h").replace("<H", " <H")
        		.replace("<br", " <br").replace("<BR", " <BR")
        		.replace("<td", " <td").replace("<TD", " <TD")
        		.replace("<th", " <th").replace("<TH", " <TH");
        Pattern p_html = Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
        Matcher m_html = p_html.matcher(htmlStr);
        htmlStr = m_html.replaceAll(""); //过滤html标签
        
        return HTMLDecode(htmlStr.trim());
	}
	
	/**
	 * 用指定分隔符分隔字符串
	 * 
	 * @param str
	 * @param separator
	 * @return
	 */
	public static List<String> segmentationStr(String str, String separator) {
		List<String> result = new ArrayList<String>();
		if (isEmpty(str))
			return result;
		StringTokenizer token = new StringTokenizer(str, separator);
		while (token.hasMoreElements()) {
			result.add(token.nextToken());
		}
		return result;
	}

	/**
	 * URL编码方法
	 * 
	 * @param url
	 * @param encode
	 *            字符集 默认为UTF-8
	 * @return
	 */
	public static String encodeURL(String url, String encode) {
		if (isEmpty(encode))
			encode = "utf8";
		try {
			return java.net.URLEncoder.encode(url, encode);
		} catch (UnsupportedEncodingException e) {
			return url;
		}
	}

	/**
	 * URL解码方法
	 * 
	 * @param url
	 * @param decode
	 *            字符集 默认为UTF-8
	 * @return
	 */
	public static String decodeURL(String url, String decode) {
		String u = url;
		if (isEmpty(decode))
			decode = "utf8";
		try {
			u = java.net.URLDecoder.decode(url, decode);
		} catch (Exception e) {
			return url;
		}
		return u;
	}

	/**
	 * 判断字符串是否为合法手机号 11位 13 15 18开头
	 * 
	 * @param str
	 * @return boolean
	 */
	public static boolean isMobile(String str) {
		if (isEmpty(str))
			return false;
		return str.matches("^(13|15|18)\\d{9}$");
	}

	/**
	 * 判断是否为正确的邮件格式
	 * 
	 * @param str
	 * @return boolean
	 */
	public static boolean isEmail(String str) {
		if (isEmpty(str))
			return false;
		return str.matches("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$");
	}

	public static boolean isPhone(String str) {
		if (isEmpty(str))
			return false;
		return str
				.matches("^((\\(\\d{3}\\))|(\\d{3}\\-))?(\\(0\\d{2,3}\\)|0\\d{2,3}-)?[1-9]\\d{6,7}$");
	}

	/**
	 * 取得指定位数随机数
	 * 
	 * @param n
	 *            指定位数
	 * @return
	 */
	public static String getRandom(int n) {
		String[] seed = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
		StringBuffer sb = new StringBuffer(n);
		for (int i = 0; i < n; i++) {
			sb.append(seed[(int) (Math.random() * 10)]);
		}
		return sb.toString();
	}

	/**
	 * URL参数编码
	 * 
	 * @param url
	 * @return
	 */
	public static String encodeURLParam(String url, String charset) {
		int rex = url.indexOf("=");
		if (rex == -1)
			return url;

		StringBuffer sb = new StringBuffer();
		List<String> list = segmentationStr(url, "=");
		for (int i = 0; i < list.size(); i++) {
			String temp = list.get(i);
			if (i == 0)
				sb.append(temp).append("=");
			else {
				if (temp.indexOf("&") == 0) {
					sb.append(temp).append("=");
				} else if (temp.indexOf("&") == -1)
					sb.append(encodeURL(temp, charset));
				else {
					sb.append(
							encodeURL(
									temp.substring(0, temp.indexOf("&")),
									charset)).append(
							temp.substring(temp.indexOf("&")));
					sb.append("=");
				}
			}
		}
		return sb.toString();
	}

	/**
	 * 全角字符转半角
	 * 
	 * @param str
	 * @return
	 */
	public static String converFullStr(String str) {
		char[] c = str.toCharArray();
		for (int i = 0; i < c.length; i++) {
			if (c[i] == 12288) {
				c[i] = (char) 32;
				continue;
			}
			if (c[i] > 65280 && c[i] < 65375)
				c[i] = (char) (c[i] - 65248);
		}
		return new String(c);
	}

	/**
	 * 去除字符串中的HTML代码
	 * 
	 * @param str
	 * @return
	 */
	public static String removeHTMLCode(String str) {
		if (isEmpty(str))
			return "";
		StringBuffer sb = new StringBuffer(str);
		try {
			while (sb.indexOf("<") != -1 && sb.indexOf(">") != -1
					&& sb.indexOf("<") < sb.indexOf(">") + 1) {
				sb.delete(sb.indexOf("<"), sb.indexOf(">") + 1);
			}
		} catch (Exception ex) {
			System.out.println(sb.length());
			System.out.println(sb.indexOf("<"));
			System.out.println(sb.indexOf(">") + 1);
			ex.printStackTrace();
			return "";
		}
		return sb.toString();
	}

	/**
	 * 去除含有html脚本的参数
	 * 
	 * @param str
	 * @return
	 */
	public static String removeHtml(String str) {
		String htmlStr = str; // 含html标签的字符串
		String textStr = "";
		java.util.regex.Pattern p_script;
		java.util.regex.Matcher m_script;
		java.util.regex.Pattern p_style;
		java.util.regex.Matcher m_style;
		java.util.regex.Pattern p_html;
		java.util.regex.Matcher m_html;
		try {
			String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>"; // 定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script>
																										// }
			String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>"; // 定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style>
																									// }
			String regEx_html = "<[^>]+>"; // 定义HTML标签的正则表达式
			p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
			m_script = p_script.matcher(htmlStr);
			htmlStr = m_script.replaceAll(""); // 过滤script标签
			p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
			m_style = p_style.matcher(htmlStr);
			htmlStr = m_style.replaceAll(""); // 过滤style标签
			p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
			m_html = p_html.matcher(htmlStr);
			htmlStr = m_html.replaceAll(""); // 过滤html标签
			/* 空格 —— *///
			p_html = Pattern.compile("\\ ", Pattern.CASE_INSENSITIVE);
			m_html = p_html.matcher(htmlStr);
			htmlStr = htmlStr.replaceAll(" ", " ");
			textStr = htmlStr;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return textStr.replace(">", "").replace("<", "");
	}

	/**
	 * 获取能兼容发布路径：“”、“/”、“/qrzk”、“/qrzk/”的应用上下文路径。只要保证应用上下文路径不是以“/”结尾就可以了
	 * 
	 * @param request
	 * @return 不以“/”结尾的应用上下文路径，例如：“”、“/qrzk”
	 */
	public static String getBasePath(HttpServletRequest request) {
		String basePath = request.getContextPath();
		// System.out.println("basePath=" + basePath);
		if (basePath.endsWith("/")) {
			basePath = basePath.substring(0, basePath.lastIndexOf('/'));
		}
		return basePath;
	}

	/**
	 * 根据字段名获取对应的get方法，例如name对应的get方法：getName
	 * 
	 * @param property
	 * @return
	 */
	public static String getterMethodName(String property) {
		return "get" + property.substring(0, 1).toUpperCase()
				+ property.substring(1);
	}

	public static void close(final AutoCloseable closeable) {
		if (closeable != null) {
			try {
				closeable.close();
			} catch (Exception e) {
				System.err.println(e.getMessage());
			}
		}
	}

	public static void close(final Closeable closeable) {
		if (closeable != null) {
			try {
				closeable.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static void close(final XMLWriter writer) {
		if (writer != null) {
			try {
				writer.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static void delete(File file) {
		if (file != null && file.exists()) {
			try {
				FileUtils.forceDelete(file);
			} catch (Exception e) {
				System.err.println("无法删除文件(夹)：" + file.getPath() + "，请手动删除!");
				e.printStackTrace();
			}
		}
	}

	public static void release(DocxReader docxReader) {
		if (docxReader != null) {
			docxReader.releaseTemporaries();
		}
	}

	/**
	 * <p>
	 * 把select SQL语句转换成count SQL语句，例如select * from t_user u where u.type=? order
	 * by u.name，转成 SELECT COUNT(1) FROM t_user u where u.type=?
	 * </p>
	 * <b>注意：</b>
	 * <p>
	 * 1.不支持直接union的SQL语句，例如：select a.* from AAA a union select b.* from BBB b，
	 * 但支持这样的SQL语句，例如：select c.* from (select a.* from AAA a union select b.*
	 * from BBB b) c；
	 * </p>
	 * <p>
	 * 2.不支持直接DISTINCT的SQL语句，例如：select DISTINCT a.* from AAA a，
	 * 但支持这样的SQL语句：select b.* from (select DISTINCT a.* from AAA a) b
	 * </p>
	 * 
	 * @author 马涛
	 * @param sql
	 * @return
	 */
	public static String toCountSql(String sql) {
		sql = sql.trim().replace('\n', ' ').replace('\t', ' ');
		while (sql.contains("  ")) {
			sql = sql.replace("  ", " ");
		}
		List<Integer> selectList = new ArrayList<Integer>();// 采用select-from栈方式查找第一个select对应的from
		String fromPart = sql.substring("select ".length());// 默认开头就是select字符串，在selectList栈中放入一个标识，同时字符串截取掉select部分
		selectList.add(1);
		{// 去掉末尾的order by部分
			int orderIndex = fromPart.toLowerCase().indexOf(" order by ");
			if (orderIndex > 0) {
				fromPart = fromPart.substring(0, orderIndex);
			}
		}
		while (true) {
			int selectIndex = fromPart.toLowerCase().indexOf(" select ");
			if (selectIndex == -1) {
				selectIndex = fromPart.toLowerCase().indexOf("(select ");
			}
			int fromIndex = fromPart.toLowerCase().indexOf(" from ");
			if (selectIndex > -1 && selectIndex < fromIndex) {
				selectList.add(1);
				fromPart = fromPart
						.substring(selectIndex + " select ".length());
			} else {
				selectList.remove(selectList.size() - 1);
				fromPart = fromPart.substring(fromIndex + " from ".length());
			}
			if (selectList.size() == 0) {
				break;
			}
		}
		String newSql = "SELECT COUNT(1) FROM " + fromPart;
		return newSql;
	}

	/**
	 * 生成不带“-”的UUID字符串
	 * 
	 * @return
	 */
	public static String generalUUID() {
		return UUID.randomUUID().toString().replace("-", "");
	}

	/**
	 * 格式化数值，数字后面没有0的时候就不要出现0
	 * 
	 * @param num
	 * @return
	 */
	public static String formatDouble(Object num) {
		if(num == null){
			return "";
		}
		num = toDouble(num, 0);
		DecimalFormat df = new DecimalFormat("#.##");
		return df.format(num);
	}

	/**
	 * 格式化分数，数字后面没有0的时候就不要出现0
	 * 
	 * @param num
	 * @return
	 */
	public static String formatScore(Object num) {
		return formatDouble(num);
	}

	// 图片转化成base64字符串
	public static byte[] getImageByteArr(File imgFile) throws IOException {// 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
		InputStream is = null;
		try {
			is = new FileInputStream(imgFile);
			return Base64.encodeBase64(IOUtils.toByteArray(is));
		} finally {
			close(is);
		}
	}

	// 图片转化成base64字符串
	public static byte[] getImageByteArr(File imgFile,String imgType) throws IOException {// 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
		InputStream is = null;
		try {
			if(imgType.equals(Consts.ImgType.wmf)){
//				WMF
			}
			is = new FileInputStream(imgFile);
			return Base64.encodeBase64(IOUtils.toByteArray(is));
		} finally {
			close(is);
		}
	}

	// base64字符串转化成图片
	public static void generateImage(String imgStr, File distImg)
			throws IOException { // 对字节数组字符串进行Base64解码并生成图片
		OutputStream os = null;
		try {
			byte[] arr = Base64.decodeBase64(imgStr);
			os = new FileOutputStream(distImg, false);
			IOUtils.write(arr, os);
		} finally {
			close(os);
		}
	}
	
	/**
	 * 返回HTML支持的颜色格式
	 * 
	 * @param color
	 *            如果为null或者auto，返回空字符串
	 * @return
	 */
	public static String getRealColor(String color) {
		if (color == null || color.equals("auto")) {
			color = "";
		} else if (isRgbColor(color)) {
			color = "#" + color;
		}
		return color;
	}

	public static boolean isRgbColor(String color) {
		color = color.toUpperCase();
		if (color.length() == 3 || color.length() == 6) {
			for (int i = 0; i < color.length(); i++) {
				char flag = color.charAt(i);
				if (!((flag >= '0' && flag <= '9') || (flag >= 'A' && flag <= 'F'))) {
					return false;
				}
			}
			return true;
		} else {
			return false;
		}
	}

	public static String getBorderWidth(String sz) {
		if (sz == null || sz.equals("")) {
			return "0pt";
		}
		int width = Integer.parseInt(sz);
		double pxW = width / 8.0;
		if (pxW < 1) {
			pxW = 1;
		}
		return pxW + "pt";
	}

	public static String getBorderStyle(String style) {
		if (style == null
				|| (!style.equals("dotted") && !style.equals("dashed") && !style
						.equals("double"))) {
			style = "solid";
		}
		return style;
	}

	public static String toUnicode(char c) {
		return "&#" + (c - 0) + ";";
	}

	public static String toUnicode(String s) {
		StringBuffer uc = new StringBuffer();
		for (int i = 0; i < s.length(); i++) {
			uc.append(toUnicode(s.charAt(i)));
		}
		return uc.toString();
	}

	public static String toJavaFormat(String wordFormat) {
		StringBuffer javaFormat = new StringBuffer();
		boolean flag = false;// 下一位字符是否是数字
		for (int i = 0; i < wordFormat.length(); i++) {
			char c = wordFormat.charAt(i);
			javaFormat.append(c);
			char nc = 0;// 下一个字符
			if (i < wordFormat.length() - 1) {
				nc = wordFormat.charAt(i + 1);
			}
			int cha = nc - '0';// 与字符'0'的差值
			if (c == '%') {
				if (cha >= 0 && cha <= 9) {
					flag = true;
				} else {
					javaFormat.append('%');
				}
			}
			if (flag
					&& ((i == wordFormat.length() - 1) || (cha < 0 || cha > 9))) {
				javaFormat.append("$s");
				flag = false;
			}
		}
		// if(s.length() > 0){
		// javaFormat.append("%");
		// if(s.charAt(0) >= 48 && s.charAt(0) <= 57){
		// boolean flag = true;//下一位字符是否是数字
		// for(int i=0;i<s.length();i++){
		// javaFormat.append(s.charAt(i));
		// if(flag){
		// if((i == s.length() - 1) || (s.charAt(i + 1) < 48 || s.charAt(i + 1)
		// > 57)){
		// javaFormat.append('$');
		// flag = false;
		// }
		// }
		// }
		// }else{
		// javaFormat.append(s);
		// }
		// }
		// }
		return javaFormat.toString();
	}

	/**
	 * 把行、列的index值转成Excel坐标
	 * 
	 * @param rowIndex 行序列值，从0开始计数
	 * @param colIndex 列序列值，从0开始计数
	 * @return
	 */
	public static String toExcelCoordinate(int rowIndex, int colIndex) {
		char A = 'A';
		int m = colIndex / 26;
		String priStr = m == 0 ? "" : (((char) (A + (m - 1))) + "");
		colIndex = colIndex % 26;
		String colStr = ((char) (A + colIndex)) + "";
		return priStr + colStr + (rowIndex + 1);
	}

	/**
	 * 把整数序列值转成大写字母序列A、B、C、D…… TODO 现仅支持到Z
	 * 
	 * @param num
	 *            1、2、3、4……
	 * @return
	 */
	public static String toUpperLetter(Integer num) {
		char letter = 'A';
		letter += (num - 1);
		return String.valueOf(letter);
	}

	/**
	 * 把整数序列值转成小写字母序列a、b、c、d…… TODO 现仅支持到z
	 * 
	 * @param num
	 *            1、2、3、4……
	 * @return
	 */
	public static String toLowerLetter(Integer num) {
		return toUpperLetter(num).toLowerCase();
	}

	private final static String rnums[] = { "m", "cm", "d", "cd", "c", "xc",
			"l", "xl", "x", "Mx", "v", "Mv", "M", "CM", "D", "CD", "C", "XC",
			"L", "XL", "X", "IX", "V", "IV", "I" }; // 儲存所有羅馬數字
	private final static int anums[] = { 1000000, 900000, 500000, 400000,
			100000, 90000, 50000, 40000, 10000, 9000, 5000, 4000, 1000, 900,
			500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; // 儲存羅馬數字表示的值

	/**
	 * 將整數轉成以字串表示的大写羅馬數字
	 * 
	 * @param num
	 * @return
	 */
	public static String toUpperRoman(Integer num) {
		if (num == 0) { // 因為羅馬數字裡並沒有零，所以輸出ZERO！
			return "ZERO";
		} else if (num < 0 || num > 3999999) { // 溢位判斷
			return "OVERFLOW";
		}

		StringBuilder output = new StringBuilder(); // 儲存羅馬數字字串
		for (int i = 0; num > 0 && i < anums.length; i++) { // 尋找對應的羅馬數字
			while (num >= anums[i]) { // 將羅馬數字加到output物件內
				num -= anums[i];
				output.append(rnums[i]);
			}
		}
		return output.toString().toUpperCase(); // 傳回羅馬數字字串
	}

	/**
	 * 將整數轉成以字串表示的小写羅馬數字
	 * 
	 * @param num
	 * @return
	 */
	public static String toLowerRoman(Integer num) {
		return toUpperRoman(num).toLowerCase();
	}

	/**
	 * 将数字转化为汉字的数组,因为各个实例都要使用所以设为静态
	 */
	private static final char[] cnUpperNumbers = { '零', '壹', '贰', '叁', '肆',
			'伍', '陆', '柒', '捌', '玖' };

	/**
	 * 将数字转化为汉字的数组,因为各个实例都要使用所以设为静态
	 */
	private static final char[] cnLowerNumbers = { '零', '一', '二', '三', '四',
			'五', '六', '七', '八', '九' };

	/**
	 * 供分级转化的数组,因为各个实例都要使用所以设为静态
	 */
	private static final char[] series = { '十', '百', '千', '万', '十', '百', '千',
			'亿' };

	/**
	 * 阿拉伯数字转成大写中文的数字。
	 * 
	 * @param num
	 * @return
	 */
	public static String toCNUpperNum(Integer num) {
		return toCNNum(num, cnUpperNumbers);
	}

	/**
	 * 阿拉伯数字转成小写中文的数字。
	 * 
	 * @param num
	 * @return
	 */
	public static String toCNLowerNum(Integer num) {
		return toCNNum(num, cnLowerNumbers);
	}

	/**
	 * 阿拉伯数字转成中文的数字，不涉及金额数字的处理。TODO 有一些Bug，对100,1001类似的特殊数字没有处理好。
	 * 现在100以内的数字或者中间不带0的数字转换没有问题。 <b>*不支持负数</b>
	 * 
	 * @param num
	 *            阿拉伯数据
	 * @return 中文数字
	 */
	private static String toCNNum(int num, char[] cnNumbers) {
		if (num == 0) {
			return cnNumbers[0] + "";
		}
		if (num < 0) {
			throw new RuntimeException("不支持负数");
		}
		// 成员变量初始化
		String integerPart = "" + num;

		// 因为是累加所以用StringBuffer
		StringBuffer sb = new StringBuffer();

		// 整数部分处理
		for (int i = 0; i < integerPart.length(); i++) {
			int number = getNumber(integerPart.charAt(i));

			sb.append(cnNumbers[number]);
			if (integerPart.length() - 2 - i >= 0) {
				sb.append(series[integerPart.length() - 2 - i]);
			}
		}
		String cnNum = sb.toString();
		if (cnNum.startsWith("一十")) {
			cnNum = cnNum.substring(1);
		}
		while (cnNum.endsWith("" + cnNumbers[0])) {
			cnNum = cnNum.substring(0, cnNum.length() - 1);
		}

		// 返回拼接好的字符串
		return cnNum;
	}

	/**
	 * 将字符形式的数字转化为整形数字
	 * 
	 * @param c
	 * @return
	 */
	private static int getNumber(char c) {
		String str = String.valueOf(c);
		return Integer.parseInt(str);
	}

	private static LayoutContext defalutLc = null;
	static{
		LayoutContextImpl myLc = new LayoutContextImpl(LayoutContextImpl.getDefaultLayoutContext());
		myLc.setParameter(Parameter.SCRIPTLEVEL, -1);
		defalutLc = new StyleAttributeLayoutContext(myLc, "16pt", Color.BLACK);
	}
	private static String outFileType = "image/png";
	private static String encoding = "utf-8";

	/**
	 * 把MathML表达的公式打印成png，然后转成base64数据
	 * 
	 * @param mathMlStr
	 * @return
	 * @throws IOException
	 */
	public static byte[] convertToBase64Img(StringBuffer mathMlStr)
			throws IOException {
		// System.out.println(mathMlStr);
		File xmlFile = null;
		File imgFile = null;
		FileOutputStream xmlFos = null;
		try {
			xmlFile = new File(ParamUtils.TemporaryDirPath, UUID.randomUUID()
					.toString());
			imgFile = new File(ParamUtils.TemporaryDirPath, UUID.randomUUID()
					.toString());
			xmlFos = new FileOutputStream(xmlFile);
			IOUtils.write(mathMlStr, xmlFos, encoding);
			xmlFos.flush();
			Converter.getInstance().convert(xmlFile, imgFile, outFileType, defalutLc);
			return getImageByteArr(imgFile);
		} finally {
			close(xmlFos);
			delete(xmlFile);
			delete(imgFile);
		}
	}

	public static int toInteger(Object obj, int defaultValue) {
		try {
			return (int) toDouble(obj,defaultValue);
		} catch (Exception e) {
			return defaultValue;
		}
	}

	public static double toDouble(Object obj, double defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (obj instanceof Integer) {
			return (Integer) obj;
		}
		if (obj instanceof Float) {
			return (Float) obj;
		}
		if (obj instanceof Double) {
			return (Double) obj;
		}
		try {
			return Double.parseDouble(obj.toString());
		} catch (Exception e) {
			return defaultValue;
		}
	}
	
	public static boolean toBoolean(Object obj, boolean defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		String v = obj.toString().toLowerCase();
		return !(v.equals("0") || v.equals("false") || v.equals("no") || v.equals("none") || v.equals("f") || v.equals("n"));
	}

	/***************************************
	 * 首字母大写
	 */
	public static String upperFirstChar(String source) {
		if (isEmpty(source))
			return "";
		if (source.length() == 1)
			return source.toUpperCase();
		return source.substring(0, 1).toUpperCase() + source.substring(1);
	}

	/*****************************************
	 * 首字母小写
	 */
	public static String lowerFirstChar(String source) {
		if (isEmpty(source))
			return "";
		if (source.length() == 1)
			return source.toUpperCase();
		return source.substring(0, 1).toUpperCase() + source.substring(1);
	}
	
	/**
	 * 自动格式化html代码，并自动纠正里面不完整的标签；把body里面的meta、title、style、link标签放到head标签里面；去掉script标签
	 * @param htmlStr
	 */
	public static String formatHtml(String htmlStr){
		htmlStr = repairHtmlStr(htmlStr.replace(" data-mathml=\"<math\"", ""));
		htmlStr = repairHtmlNode(htmlStr);
		htmlStr = repairHtmlFormula(htmlStr);
		htmlStr = repairHtmlStr(htmlStr);
		return htmlStr;
	}
	
	private static String repairHtmlStr(String htmlStr){
		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());
			}
		}
		if(!htmlStr.toLowerCase().contains("</body>")){
			htmlStr = "<html><body>" + htmlStr + "</body></html>";
		}
		if(!htmlStr.contains("<!ENTITY nbsp \" \">")){
			htmlStr = Consts.HtmlSymbolDocTypeStr + htmlStr;
		}
		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);
			}
		}
		return htmlStr;
	}
	
	private static String repairHtmlNode(String htmlStr){
		try{
			String newHtmlStr = htmlStr.replace("&nbsp;","##MT-NBSP##");
			SAXReader sax = new SAXReader();
	        ByteArrayInputStream is = new ByteArrayInputStream(newHtmlStr.getBytes("UTF-8"));
	        Document document = sax.read(is);
	        Element root = document.getRootElement();
	        Element head = root.element("head");
	        if(head == null){
	        	List elements = root.content();
	        	head = DocumentHelper.createElement("head");
	        	elements.add(0,head);
	        	root.setContent(elements);
	        }
	        repairHtmlNode(head,root.element("body"));
	        newHtmlStr = root.asXML().replace("##MT-NBSP##", "&nbsp;");
	        return newHtmlStr;
		}catch(Exception e){
			log.warn("修复HTML字符串失败，原因：" + e.getMessage(), e);
			return htmlStr;
		}
	}
	
	/**
	 * <p>清除公式图片，保留latex表达式</p>
	 * @author songyue
	 * @date
	 */
	public static String repairHtmlFormula(String htmlStr){
		try{
			org.jsoup.nodes.Document doc = Jsoup.parse(htmlStr);
			Elements img = doc.select("img[class=kfformula]");
			for(org.jsoup.nodes.Element e : img){
				String latex = e.attr("data-latex");
				org.jsoup.nodes.Element spantex = doc.createElement("span");
				spantex.html("\\["+latex+"\\]");
				e.replaceWith(spantex);
			}
			htmlStr = doc.html();
			htmlStr = htmlStr.replaceAll("\\n", "").replace("<body>  ", "<body>").replace(" </body>", "</body>");
	        return htmlStr;
		}catch(Exception e){
			log.warn("修复HTML字符串失败，原因：" + e.getMessage(), e);
			return htmlStr;
		}
	}
	
	/**
	 * 去掉script标签；把meta、title、style、link标签放到head标签里面
	 * @param head
	 * @param htmlNode
	 */
	private static void repairHtmlNode(Element head,Element htmlNode){
		List elements = htmlNode.content();
		if(elements != null && elements.size() > 0){
			for(int i=0;i<elements.size();i++){
				Object childObj = elements.get(i);
				if(childObj instanceof Element){
					Element child = (Element)childObj;
					String tagName = child.getName().toLowerCase();
					if(tagName.equals("meta") || tagName.equals("title") || tagName.equals("style") || tagName.equals("link")){
						head.add((Element)elements.remove(i));
						i--;
					}else if(tagName.equals("script")){
						elements.remove(i);
						i--;
					}else{
						repairHtmlNode(head,child);
					}
				}
			}
			htmlNode.setContent(elements);
		}
	}
	
	/**
	 * 把试题里面的body、answer、option.content字段补全
	 * @param ques
	 */
	public static void repairQuestionAndOptions(Question ques){
		if(ques.getBodyWord() != null && ques.getBodyWord().length > 0 && isEmpty(ques.getBody())){
			try{
				ques.setBody(HtmlCreator.covertToHtml(ques.getBodyWord()).toString());
			}catch(Exception e){
				log.warn("试题[id=" + ques.getId() + "]的word数据无法转换成html代码，原因：" + e.getMessage(), e);
				ques.setBody("<span style='color:red'>" + String.format(Consts.ERR_QUS_MSG, ques.getId() + "") + "</span>");
			}
		}

		if(ques.getAnswerWord() != null && ques.getAnswerWord().length > 0 && isEmpty(ques.getAnswer())){
			try{
				ques.setAnswer(HtmlCreator.covertToHtml(ques.getAnswerWord()).toString());
			}catch(Exception e){
				log.warn("试题[id=" + ques.getId() + "]的word答案无法转换成html代码，原因：" + e.getMessage(), e);
				ques.setAnswer("<span style='color:red'>" + String.format(Consts.ERR_ANSWER_MSG, ques.getId() + "") + "</span>");
			}
		}
		
		if(ques.getOptions() != null){
			for(QuestionOption qo:ques.getOptions()){
				if(qo.getContentWord() != null && qo.getContentWord().length > 0 && isEmpty(qo.getContent())){
					try{
						qo.setContent(HtmlCreator.covertToHtml(qo.getContentWord()).toString());
					}catch(Exception e){
						log.warn("试题[id=" + ques.getId() + "]的选项[id=" + qo.getId() + "]word数据无法转换成html代码，原因：" + e.getMessage(), e);
						qo.setContent("<span style='color:red'>" + String.format(Consts.ERR_QUS_OPT_MSG, ques.getId() + "") + "</span>");
					}
				}
			}
		}
	}
	
	/**
	 * 把试题里面的Word数据转换成Html数据，并且把里面的Word字段清空掉，减少对象体积
	 * @param ques
	 */
	public static void quesWordToHtmlForShow(Question ques){
		if(ques.getBodyWord() != null && ques.getBodyWord().length > 0 && isEmpty(ques.getBody())){
			try{
				ques.setBody(HtmlCreator.covertToHtml(ques.getBodyWord()).toString());
			}catch(Exception e){
				log.warn("试题[id=" + ques.getId() + "]的word数据无法转换成html代码，原因：" + e.getMessage(), e);
				ques.setBody("<span style='color:red'>" + String.format(Consts.ERR_QUS_MSG, ques.getId() + "") + "</span>");
			}
		}
		ques.setBodyWord(null);
		if(ques.getAnswerWord() != null && ques.getAnswerWord().length > 0 && isEmpty(ques.getAnswer())){
			try{
				ques.setAnswer(HtmlCreator.covertToHtml(ques.getAnswerWord()).toString());
			}catch(Exception e){
				log.warn("试题[id=" + ques.getId() + "]的word答案无法转换成html代码，原因：" + e.getMessage(), e);
				ques.setAnswer("<span style='color:red'>" + String.format(Consts.ERR_ANSWER_MSG, ques.getId() + "") + "</span>");
			}
		}
		ques.setAnswerWord(null);
		if(ques.getOptions() != null && ques.getOptions().size() > 0){
			String answer = "";
			for(QuestionOption qo:ques.getOptions()){
				if(qo.getContentWord() != null && qo.getContentWord().length > 0 && isEmpty(qo.getContent())){
					try{
						qo.setContent(HtmlCreator.covertToHtml(qo.getContentWord()).toString());
					}catch(Exception e){
						log.warn("试题[id=" + ques.getId() + "]的选项[id=" + qo.getId() + "]word数据无法转换成html代码，原因：" + e.getMessage(), e);
						qo.setContent("<span style='color:red'>" + String.format(Consts.ERR_QUS_OPT_MSG, ques.getId() + "") + "</span>");
					}
				}
				qo.setContentWord(null);
				if(qo.getIsCorrectOption() == 1){
					answer = answer + qo.getOptionNoString() + "、";
				}
			}
			if(answer.length() > 0){
				answer = answer.substring(0,answer.length() - 1);
			}
			ques.setAnswer(answer);
		}
	}
	
	/**
	 * 新建一个临时文件。注意：只是声明了一个文件对象，没有实际创建物理文件。
	 * @return
	 */
	public static File newTmpFile(){
		return new File(ParamUtils.TemporaryDirPath,UUID.randomUUID().toString().replace("-", ""));
	}
	
	/**
	 * 新建一个存放在附件目录中的文件。注意：只是声明了一个文件对象，没有实际创建物理文件。
	 * @return
	 */
	public static File newAttFile(){
		return newAttFile(UUID.randomUUID().toString());
	}
	
	/**
	 * 按照UUID命名，新建一个存放在附件目录中的文件。注意：只是声明了一个文件对象，没有实际创建物理文件。
	 * 如果文件存在，则删除之
	 * @param uuid 32位或者64位的UUID码，以此来命名附件。如果是36位的UUID，程序会自动去掉中间的“-”
	 * @return
	 */
	public static File newAttFile(String uuid){
		String newFileName = uuid.replace("-", "");
		if(newFileName.length() != 32){
			throw new RuntimeException("非法的UUID：" + uuid);
		}
		File storDir = new File(ParamUtils.AttDirPath,newFileName.substring(0, 2));
		storDir = new File(storDir,newFileName.substring(2, 4));
		storDir = new File(storDir,newFileName.substring(4, 6));
		if(!storDir.exists()){
			storDir.mkdirs();
		}
		File newFile = new File(storDir,newFileName);
		if(newFile.exists()){
			log.warn("文件：" + newFile.getPath() + "已存在，将被删除。");
			newFile.delete();
		}
		return newFile;
	}
	
	/**
	 * 新建一个存放在附件目录中的文件带后缀。注意：只是声明了一个文件对象，没有实际创建物理文件。
	 * @return
	 */
	public static File newTempFile(String suffix){
		return newTempFile(UUID.randomUUID().toString(), suffix);
	}
	
	/**
	 * 按照UUID命名，新建一个存放在附件目录中的文件带后缀。注意：只是声明了一个文件对象，没有实际创建物理文件。
	 * 如果文件存在，则删除之
	 * @param uuid 32位或者64位的UUID码，以此来命名附件。如果是36位的UUID，程序会自动去掉中间的“-”
	 * @param suffix
	 * @return
	 */
	public static File newTempFile(String uuid, String suffix){
		String newFileName = uuid.replace("-", "");
		if(newFileName.length() != 32){
			throw new RuntimeException("非法的UUID：" + uuid);
		}
		File storDir = new File(ParamUtils.AttDirPath,newFileName.substring(0, 2));
		storDir = new File(storDir,newFileName.substring(2, 4));
		storDir = new File(storDir,newFileName.substring(4, 6));
		if(!storDir.exists()){
			storDir.mkdirs();
		}
		File newFile = new File(storDir,newFileName + "." + suffix);
		if(newFile.exists()){
			log.warn("文件：" + newFile.getPath() + "已存在，将被删除。");
			newFile.delete();
		}
		return newFile;
	}
	
	/**
	 * 查找第一个匹配正则表达式的字符串的位置
	 * @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;
        }
	}
	
	/**
	 * 从指定的位置开始查找第一个匹配正则表达式的字符串的位置
	 * @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;
	}
	
	public static byte[] htmlToDocx(String htmlStr) throws JAXBException, Docx4JException{
		ByteArrayOutputStream bos = null;
		try {
			bos = new ByteArrayOutputStream();
			
			RFonts rfonts = Context.getWmlObjectFactory().createRFonts();
			rfonts.setAscii("eastAsia");
	        XHTMLImporterImpl.addFontMapping("eastAsia", rfonts);
	        
	        // Create an empty docx package
			WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
			
			NumberingDefinitionsPart ndp = new NumberingDefinitionsPart();
			wordMLPackage.getMainDocumentPart().addTargetPart(ndp);
			ndp.unmarshalDefaultNumbering();		
						
			// Convert the XHTML, and add it into the empty docx we made
	        XHTMLImporterImpl xHTMLImporter = new XHTMLImporterImpl(wordMLPackage);
	        xHTMLImporter.setHyperlinkStyle("Hyperlink");
			wordMLPackage.getMainDocumentPart().getContent().addAll(xHTMLImporter.convert(formatHtml(htmlStr), ParamUtils.TemporaryDirPath) );
			//dealFormula(wordMLPackage);
			wordMLPackage.save(bos);
			return bos.toByteArray();
		}finally{
			close(bos);
		}
	}

//	/**
//	 * 在文件中设置自定义属性。<br/>
//	 * <b>*此方法需要java 7及以上版本支持，另外需要操作系统支持，不过从现在的测试结果来看Window系列的操作系统都支持</b><br/>
//	 * <b>*此方法加的自定义属性在HTTP下载的文件中就丢失了，无法满足现在的需求</b>
//	 * @param filePath 文件路径
//	 * @param attrName 自定义属性名
//	 * @param attrValue 要设置的属性值
//	 */
//	@Deprecated
//	public static void setUserDefinedAttr(String filePath,String attrName,String attrValue){
//		try {
//			Path file = FileSystems.getDefault().getPath(filePath);
//			UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
//			attributeView.write(attrName, Charset.defaultCharset().encode(attrValue));
//		} catch (Exception e) {
//			throw new RuntimeException("在文件[" + filePath + "]中设置自定义属性(name=" + attrName + ",value=" + attrValue + ")失败",e);
//		}
//	}
//
//	/**
//	 * 从文件中获取自定义属性值。如果文件不存在或者属性值不存在，直接返回null。<br/>
//	 * <b>*此方法需要java 7及以上版本支持，另外需要操作系统支持，不过从现在的测试结果来看Window系列的操作系统都支持</b><br/>
//	 * <b>*此方法加的自定义属性在HTTP下载的文件中就丢失了，无法满足现在的需求</b>
//	 * @param filePath 文件路径
//	 * @param attrName 自定义属性名
//	 * @return 属性值。如果文件不存在或者属性值不存在，直接返回null
//	 */
//	@Deprecated
//	public static String getUserDefinedAttr(String filePath,String attrName){
//		try {
//			Path file = FileSystems.getDefault().getPath(filePath);
//			UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
//			ByteBuffer buf = ByteBuffer.allocate(attributeView.size(attrName));  
//			attributeView.read(attrName, buf);  
//			buf.flip();  
//			return Charset.defaultCharset().decode(buf).toString();  
//		} catch (NoSuchFileException e) {
//			return null;
//		} catch (Exception e) {
//			throw new RuntimeException("从文件[" + filePath + "]中获取自定义属性(name=" + attrName + ")失败",e);
//		}
//	}
	
	/**
	 * 在Docx文件中设置属性，这些属性一定要是docx自带的属性：dc:creator、cp:lastModifiedBy、cp:revision、dcterms:created、dcterms:modified、cp:version。如果是docx不识别的属性，那么打开Word时会报错<br/>
	 * @param docxFile docx文件
	 * @param attrName 自定义属性名
	 * @param attrValue 要设置的属性值
	 */
	public static void setDocxDefinedAttr(File docxFile,String attrName,String attrValue){
		DocxReader docxReader = null;
		OutputStream os = null;
		OutputStreamWriter osw = null;
		XMLWriter writer = null;
		try {
			docxReader = new DocxReader(docxFile);
			docxReader.doRead();
			ResourceInfo docPropsCore = docxReader.getDocPropsCore();
			SAXReader sax = new SAXReader();  
	        Document xmlDoc = sax.read(docPropsCore.getFile());  
	        Element root = xmlDoc.getRootElement();//根节点
	        QName attrQName = null;
			{
				int flag = attrName.indexOf(':');
				if(flag > 0){
					@SuppressWarnings("rawtypes")
					List nsList = root.declaredNamespaces();
					Map<String,Namespace> nsMap = new HashMap<String, Namespace>();
					for(Object nsObj:nsList){
						Namespace ns = (Namespace)nsObj;
						nsMap.put(ns.getPrefix(), ns);
					}
					attrQName = new QName(attrName.substring(flag + 1),nsMap.get(attrName.substring(0, flag)));
				}else{
					attrQName = new QName(attrName);
				}
			}
	        Element ele = root.element(attrQName);
	        if(ele == null){
	        	ele = root.addElement(attrQName);
	        }
	        ele.setText(attrValue);
	        OutputFormat format = OutputFormat.createPrettyPrint();
	        format.setEncoding("UTF-8");
	        os = new FileOutputStream(docPropsCore.getFile(), false);
	        osw = new OutputStreamWriter(os,"UTF-8");
	        writer = new XMLWriter(osw, format);
	        writer.write(xmlDoc);
	        writer.flush();
	        close(writer);
	        Map<String,ResourceInfo> needToUpdate = new HashMap<String, ResourceInfo>();
	        needToUpdate.put(docPropsCore.getPathInZip(), docPropsCore);
	        DocxReader.updateDocx(docxFile, needToUpdate);
		} catch (Exception e) {
			throw new RuntimeException("在Docx文件[" + docxFile.getPath() + "]中设置属性(name=" + attrName + ",value=" + attrValue + ")失败",e);
		} finally{
			close(os);
			close(osw);
			close(writer);
			release(docxReader);
		}
	}

	/**
	 * 从Docx文件中获取属性值。如果属性值不存在，直接返回null。<br/>
	 * 常见的docx的属性：dc:creator、cp:lastModifiedBy、cp:revision、dcterms:created、dcterms:modified、cp:version。<br/>
	 * @param docxFile docx文件
	 * @param attrName 自定义属性名
	 * @return 属性值。如果属性值不存在，直接返回null
	 */
	public static String getDocxDefinedAttr(File docxFile,String attrName){
		DocxReader docxReader = null;
		try {
			docxReader = new DocxReader(docxFile);
			docxReader.doRead();
			ResourceInfo docPropsCore = docxReader.getDocPropsCore();
			SAXReader sax = new SAXReader();  
	        Document xmlDoc = sax.read(docPropsCore.getFile());
	        Element root = xmlDoc.getRootElement();//根节点
			QName attrQName = null;
			{
				int flag = attrName.indexOf(':');
				if(flag > 0){
					@SuppressWarnings("rawtypes")
					List nsList = root.declaredNamespaces();
					Map<String,Namespace> nsMap = new HashMap<String, Namespace>();
					for(Object nsObj:nsList){
						Namespace ns = (Namespace)nsObj;
						nsMap.put(ns.getPrefix(), ns);
					}
					attrQName = new QName(attrName.substring(flag + 1),nsMap.get(attrName.substring(0, flag)));
				}else{
					attrQName = new QName(attrName);
				}
			}
	        Element ele = root.element(attrQName);
	        return (ele == null) ? null : ele.getText();
		} catch (Exception e) {
			throw new RuntimeException("从Docx文件[" + docxFile.getPath() + "]中获取属性(" + attrName + ")失败",e);
		} finally{
			release(docxReader);
		}
	}

	/**
	 * 执行数据库备份脚本程序
	 * @throws IOException 
	 */
	public static void backupDB(){
		try {
			long cur = System.currentTimeMillis();
			Process p = Runtime.getRuntime().exec(ParamUtils.BackupDbExePath);
			while(p.isAlive() && (System.currentTimeMillis() - cur) < 1000){
				Thread.sleep(2);
			}
			if(p.isAlive()) p.destroy();
		} catch (Exception e) {
			throw new RuntimeException("执行数据库备份脚本程序(路径：" + ParamUtils.BackupDbExePath + ")失败，原因：" + e.getMessage(), e);
		}
	}
	
	/**
	 * 获取自动增长后的提卡号
	 * @param courseCode 课程Code
	 * @return
	 */
	public static synchronized String getNewQuestionNo(String courseCode){
		Integer lastIndex = Consts.CourseLastIndexMap.get(courseCode);
		if(lastIndex == null){
			lastIndex = 0;
		}
		lastIndex++;
		Consts.CourseLastIndexMap.put(courseCode, lastIndex);
		return courseCode + String.format("%04d",lastIndex);
	}
	
	public static String null2String(Object str,String defaultStr){
		if(str == null){
			return defaultStr;
		}else{
			return str + "";
		}
	}
	
	public static String null2String(Object str){
		return null2String(str,"");
	}
	
	/**
	 * 处理word内容，获取word里面的正文数据
	 * @param word
	 * @param paperDocxReader
	 * @param needToUpdate
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	public static String dealWord(byte[] word, DocxReader paperDocxReader, Map<String, ResourceInfo> needToUpdate)
            throws IOException, DocumentException {
        String content = "";
        File newDocx = null;
        DocxReader xtReader = null;
        try {
            {// 把内存的数据写入到临时文件里面，方便后面的逻辑使用
                FileOutputStream fos = null;
                try {
                    newDocx = new File(ParamUtils.TemporaryDirPath, UUID.randomUUID().toString());
                    fos = new FileOutputStream(newDocx);
                    IOUtils.write(word, fos);
                    fos.flush();
                } finally {
                    close(fos);
                }
            }
            xtReader = new DocxReader(newDocx);
            xtReader.doRead();

            {// 处理DefaultContentType
                Map<String, DefaultContentType> xtDctMap = xtReader.getDefaultContentTypeMap();
                Map<String, DefaultContentType> paperDctMap = paperDocxReader.getDefaultContentTypeMap();

                SAXReader sax = new SAXReader();
                Document xmlDoc = sax.read(paperDocxReader.getContentTypesInfo().getFile());
                Element root = xmlDoc.getRootElement();// 根节点
                boolean hasDiff = false;
                for (Map.Entry<String, DefaultContentType> dct : xtDctMap.entrySet()) {
                    if (!paperDctMap.containsKey(dct.getKey())) {
                        hasDiff = true;
                        Element newDctEle = root.addElement("Default");
                        newDctEle.addAttribute("ContentType", dct.getValue().getContentType());
                        newDctEle.addAttribute("Extension", dct.getValue().getExtension());
                        paperDctMap.put(dct.getKey(), dct.getValue());
                    }
                }
                if (hasDiff) {
                    OutputFormat format = OutputFormat.createCompactFormat();
                    format.setEncoding("UTF-8");
                    XMLWriter writer = new XMLWriter(new FileWriter(paperDocxReader.getContentTypesInfo().getFile()),
                            format);
                    writer.write(xmlDoc); // 输出到文件
                    writer.close();
                }
                needToUpdate.put(paperDocxReader.getContentTypesInfo().getPathInZip(),
                        paperDocxReader.getContentTypesInfo());
            }

            String docStr = null;
            Set<String> embedIdSet = new HashSet<String>();
            {// 处理document.xml里面的内容
                File document = xtReader.getDocumentInfo().getFile();
                InputStream is = null;
                try {
                    is = new FileInputStream(document);
                    docStr = IOUtils.toString(is, "UTF-8");
                    {
	                    Pattern pattern = Pattern.compile("r:embed=\"(.+?)\"");
	        			Matcher matcher = pattern.matcher(docStr);
	        			while(matcher.find()){
	        				embedIdSet.add(matcher.group(1));
	        			}
                    }

                    {
	                    Pattern pattern = Pattern.compile("r:id=\"(.+?)\"");
	        			Matcher matcher = pattern.matcher(docStr);
	        			while(matcher.find()){
	        				embedIdSet.add(matcher.group(1));
	        			}
                    }
                }finally{
                	close(is);
                }
            }
            
            Map<String, String> imgIdMap = new HashMap<String, String>();
            {// 处理图片资源
                List<ResourceInfo> imgs = xtReader.getImgs();
                if (imgs != null && imgs.size() > 0) {
                    SAXReader sax = new SAXReader();
                    Document xmlDoc = sax.read(paperDocxReader.getRelsInfo().getFile());
                    Element root = xmlDoc.getRootElement();// 根节点
                    for (ResourceInfo img : imgs) {
                    	if(!embedIdSet.contains(img.getId())){
                    		continue;
                    	}
                        ResourceInfo newImg = ResourceInfo.copyResourceInfo(img, paperDocxReader.getWordDir());
                        imgIdMap.put(img.getId(), newImg.getId());
                        Element newDctEle = root.addElement("Relationship");
                        newDctEle.addAttribute("Id", newImg.getId());
                        newDctEle.addAttribute("Type", newImg.getType());
                        newDctEle.addAttribute("Target", newImg.getTarget());
                        needToUpdate.put(newImg.getPathInZip(), newImg);
                    }
                    OutputFormat format = OutputFormat.createCompactFormat();
                    format.setEncoding("UTF-8");
                    XMLWriter writer = new XMLWriter(new FileWriter(paperDocxReader.getRelsInfo().getFile()), format);
                    writer.write(xmlDoc); // 输出到文件
                    writer.close();
                    needToUpdate.put(paperDocxReader.getRelsInfo().getPathInZip(), paperDocxReader.getRelsInfo());
                }
            }
            {// 处理document.xml里面的内容
            	int sectPrIndex = docStr.indexOf("<w:sectPr");
                if(sectPrIndex < 0){
                	sectPrIndex = docStr.indexOf("</w:body>");
                }
                content = docStr.substring((docStr.indexOf("<w:body>") + "<w:body>".length()),sectPrIndex);
                for (Map.Entry<String, String> entry : imgIdMap.entrySet()) {
                    content = content.replace("r:embed=\"" + entry.getKey() + "\"", "r:embed=\"" + entry.getValue()
                            + "\"").replace("r:id=\"" + entry.getKey() + "\"", "r:id=\"" + entry.getValue()
                                    + "\"");
                }
            }
        } finally {
            delete(newDocx);
            release(xtReader);
        }
        return content;
    }
	
//	public static String latex2OMML(String latex){
//		String omml = "";
//		// for latex
//		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//		/* Create vanilla SnuggleEngine and new SnuggleSession */
//		SnuggleEngine engine = new SnuggleEngine();
//		SnuggleSession session = engine.createSession();
//		/* Parse some very basic Math Mode input */
//		SnuggleInput input = new SnuggleInput(latex);
//		try {
//			session.parseInput(input);
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		/*
//		 * Convert the results to an XML String, which in this case will be a
//		 * single MathML <math>...</math> element.
//		 */
//		String mathml = session.buildXMLString();
//		System.out.println("Input " + input.getString() + " was converted to:\n" + mathml);
//		//InputStream stylesheet =  ZujuanService.class.getResourceAsStream("/paperTmps/MML2OMML.xsl");
//		File stylesheet = new File("/Users/songyue/MML2OMML.xsl");
//		StringReader sr = new StringReader(mathml);
//		InputSource mmldata = new InputSource(sr);
//		DocumentBuilder builder = null;
//		try {
//			builder = factory.newDocumentBuilder();
//		} catch (ParserConfigurationException e) {
//			e.printStackTrace();
//		}
//		try {
//			document = builder.parse(mmldata);
//		} catch (SAXException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//
//		// Use a Transformer for output
//		TransformerFactory tFactory = new TransformerFactoryImpl();
//		StreamSource stylesource = new StreamSource(stylesheet);
//		Transformer transformer = null;
//		try {
//			transformer = tFactory.newTransformer(stylesource);
//		} catch (TransformerConfigurationException e) {
//			e.printStackTrace();
//		}
//
//		DOMSource source = new DOMSource(document);
//		StringWriter writer = new StringWriter();
//		StreamResult result = new StreamResult(writer);
//		try {
//			transformer.transform(source, result);
//		} catch (TransformerException e) {
//			e.printStackTrace();
//		}
//		General.close(sr);
//		General.close(writer);
//		omml = writer.toString();
//		omml = "<m:oMath" + StringUtils.substringAfter(omml, "<m:oMath");
//		System.out.println("omml:" + omml);
//		return omml;
//	}
	
	public static StringBuffer omml2mml(Element oMath){
		String omml = oMath.asXML();
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		InputStream stylesheet = General.class.getResourceAsStream("/paperTmps/OMML2MML.XSL");
		//File stylesheet = new File("/Users/songyue/OMML2MML.xsl");
		StringReader sr = new StringReader(omml);
		InputSource ommldata = new InputSource(sr);
		DocumentBuilder builder = null;
		StringWriter writer = new StringWriter();
		try {
			builder = factory.newDocumentBuilder();
			org.w3c.dom.Document document = builder.parse(ommldata);
			// Use a Transformer for output
			TransformerFactory tFactory = new TransformerFactoryImpl();
			StreamSource stylesource = new StreamSource(stylesheet);
			Transformer transformer = null;
			transformer = tFactory.newTransformer(stylesource);
			DOMSource source = new DOMSource(document);
			StreamResult result = new StreamResult(writer);
			transformer.transform(source, result);
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			General.close(sr);
			General.close(writer);
		}
		String mml = writer.toString();
		if(isNotEmpty(mml)){
			mml = ("<mml:math" + StringUtils.substringAfter(mml, "<mml:math")).replace("mml:", "");
			System.out.println("mml:" + mml);
		}
		return new StringBuffer(mml);
	}
	
//	public static void dealFormula(WordprocessingMLPackage wordMLPackage) throws JAXBException{
//	    List<Object> paragraphs = getAllElementFromObject(wordMLPackage.getMainDocumentPart(), P.class);  
//	    String omml = "";
//	    P pToReplace = null;
//	    R rToReplace = null;
//	    int index = 0;
//	    for (Object p : paragraphs) {  
//	        List<Object> rs = getAllElementFromObject(p, R.class);
//	        for (Object r :rs) {  
//	        	List<Object> texts = getAllElementFromObject(r, Text.class);
//	        	for(Object t :texts){
//	        		Text content = (Text) t;  
//		            if (content.getValue().contains("\\[")) {
//		            	pToReplace = (P)p;
//		            	rToReplace = (R)r;
//		            	if(rToReplace != null){
//			            	omml = latex2OMML(content.getValue());
//			            	if(!omml.equals("")){
//			            		javax.xml.bind.JAXBElement omathpara = (JAXBElement) XmlUtils.unmarshalString(omml);
//				    			pToReplace.getContent().set(index, omathpara);
//			            	}
//		            	}
//		            }  
//	        	}
//	        	index++;
//	        }  
//	    }
//	}
	
	private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {  
	    List<Object> result = new ArrayList<Object>();
	    if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();
	    if (obj.getClass().equals(toSearch))
	        result.add(obj);
	    else if (obj instanceof ContentAccessor) {
	        List<?> children = ((ContentAccessor) obj).getContent();
	        for (Object child : children) {  
	            result.addAll(getAllElementFromObject(child, toSearch));
	        }  
	    }  
	    return result;
	}
	
	public static void main(String[] args) throws IOException, JAXBException, Docx4JException, DocumentException {
//		org.jsoup.nodes.Document doc = Jsoup.parse(new File("/Users/songyue/test.html"), "UTF-8");
//		String html = doc.html();
//		System.out.println(html);
//		OutputStream out = new FileOutputStream(new File("/Users/songyue/OUT_maths_test.docx"));
//		byte[] data = htmlToDocx(html);
//		IOUtils.write(data, out);
		InputStream in = new FileInputStream(new File("/Users/songyue/OUT_maths_test.docx"));
		OutputStream output = new FileOutputStream(new File("/Users/songyue/test1.html"));
		StringBuffer html = HtmlCreator.covertToHtml(IOUtils.toByteArray(in));
		IOUtils.write(html, output);
		System.out.println(html);
	}

	public static String getCellTextForShow(HSSFCell cell){
		if(cell == null){
			return Consts.NULL_STR;
		}
		switch (cell.getCellType()) {
			case HSSFCell.CELL_TYPE_BLANK:
				return Consts.NULL_STR;

			case HSSFCell.CELL_TYPE_BOOLEAN:
				return trim2Str(cell.getBooleanCellValue(), Consts.NULL_STR);

			case HSSFCell.CELL_TYPE_FORMULA:
				return trim2Str(cell.getNumericCellValue(), Consts.NULL_STR);

			case HSSFCell.CELL_TYPE_NUMERIC:
				if(DateUtil.isCellDateFormatted(cell)) {
					return formatDate(cell.getDateCellValue(), Consts.EXCEL_DATE_FORMAT_STR1);
				}else {
					BigDecimal value = new BigDecimal(cell.getNumericCellValue());
					return trim2Str(value, Consts.NULL_STR);
				}

			case HSSFCell.CELL_TYPE_STRING:
				return trim2Str(cell.getStringCellValue(), Consts.NULL_STR);

			default:
				return Consts.UNKOWN_STR;
		}
	}
	/**
	 * 格式化时间，返回格式化后的时间字符串
	 * @param date
	 * @param formatPattern
	 * @return
	 */
	public static String formatDate(Date date, String formatPattern){
		SimpleDateFormat formatter = new SimpleDateFormat(formatPattern);
		return formatter.format(date);
	}

	public static final String trim2Str(Object str,String defaultValue){
		if(str == null){
			return defaultValue;
		}
		String str2 = null;
		if(str instanceof String){
			str2 = ((String)str).trim();
		}else{
			str2 = str.toString().trim();
		}
		return str2.length() == 0 ? defaultValue:str2;
	}
}
