package com.uas.report.crystal2jasper;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.transform.TransformerException;

import org.apache.commons.io.output.FileWriterWithEncoding;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uas.report.crystal2jasper.Join.JoinType;
import com.uas.report.crystal2jasper.Link.LinkType;
import com.uas.report.crystal2jasper.Sort.SortType;
import com.uas.report.util.CollectionUtils;
import com.uas.report.util.FileUtils;
import com.uas.report.util.StringUtils;
import com.uas.report.util.ZipUtils;

public class CrystalToJasper {

	private static Logger logger = LoggerFactory.getLogger(CrystalToJasper.class);

	public static void main(String[] args) throws IOException, DocumentException, TransformerException {
		File rptZipFile = new File("C:/Users/sunyj-pc/Desktop/temp/crystal-clear/formatted");
		File outDir = new File("C:/Users/sunyj-pc/Desktop/temp/crystal-clear/formatted/out");
		File styleFile = new File("src/main/resources/crystal2jasper.xsl");
		boolean remainHierarchy = false;

		int length = args != null ? args.length : 0;
		if (length >= 1) {
			rptZipFile = new File(args[0]);
		}
		if (length >= 2) {
			outDir = new File(args[1]);
		}
		if (length >= 3) {
			styleFile = new File(args[2]);
		}
		if (length >= 4) {
			remainHierarchy = Boolean.parseBoolean(args[3]);
		}

		CrystalToJasper crystalToJasper = new CrystalToJasper();
		crystalToJasper.translate(rptZipFile, outDir, remainHierarchy, styleFile);
		logger.info("translation complete!");
	}

	/**
	 * rpt压缩文件转为jasper
	 * 
	 * @param rptZipFile
	 *            rpt压缩文件（夹）
	 * @param outDir
	 *            生成的jasper输出文件夹
	 * @param remainHierarchy
	 *            是否保持生成的jasper与rpt压缩路径层级相同
	 * @param styleFile
	 *            映射规则文件
	 * @throws IOException
	 * @throws DocumentException
	 * @throws TransformerException
	 */
	private void translate(File rptZipFile, File outDir, boolean remainHierarchy, File styleFile)
			throws IOException, DocumentException, TransformerException {
		FileUtils.checkFile(rptZipFile);
		if (rptZipFile.isFile()) {
			toJasper(rptZipFile, new File(rptZipFile.getParentFile(), "success"), outDir, remainHierarchy, styleFile);
		} else {
			toJaspers(rptZipFile, new File(rptZipFile, "success"), outDir, remainHierarchy, styleFile);
		}
	}

	/**
	 * rpt压缩文件夹转为jasper
	 * 
	 * @param rptZipDir
	 *            rpt压缩文件夹
	 * @param successDir
	 *            转换成功的rpt文件存放文件夹
	 * @param outDir
	 *            生成的jasper输出文件夹
	 * @param remainHierarchy
	 *            是否保持生成的jasper与rpt压缩路径层级相同
	 * @param styleFile
	 *            映射规则文件
	 * @throws IOException
	 */
	private void toJaspers(File rptZipDir, File successDir, File outDir, boolean remainHierarchy, File styleFile)
			throws IOException {
		FileUtils.checkDir(rptZipDir);
		File[] files = rptZipDir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				if (file == null || !file.exists()) {
					return false;
				}
				// success路径下的文件不进行转换
				String filePath = file.getAbsolutePath();
				if (filePath.endsWith("success") || filePath.contains("\\success\\")
						|| filePath.contains("/success/")) {
					return false;
				}
				// 文件必须是rpt或者zip格式
				if (file.isFile() && !filePath.endsWith(".rpt") && !filePath.endsWith(".zip")) {
					return false;
				}
				return true;
			}
		});
		for (File file : files) {
			try {
				String fileName = file.getName();
				// 是文件夹的话，递归处理
				if (file.isDirectory()) {
					// 不可直接修改successDir和outDir，否则会造成其他文件的路径有问题
					File successDirCopy = new File(successDir, fileName);
					File outDirCopy = remainHierarchy ? new File(outDir, fileName) : outDir;
					toJaspers(file, successDirCopy, outDirCopy, remainHierarchy, styleFile);
				} else {
					toJasper(file, successDir, outDir, remainHierarchy, styleFile);
				}
			} catch (Throwable e) {
				logger.error(file.getName(), e);
			}
		}
	}

	/**
	 * 单个rpt压缩文件转为jasper
	 * 
	 * @param rptZipFile
	 *            单个rpt压缩文件
	 * @param successDir
	 *            转换成功的rpt文件存放文件夹
	 * @param outDir
	 *            生成的jasper输出文件夹
	 * @param remainHierarchy
	 *            是否保持生成的jasper与rpt压缩路径层级相同
	 * @param styleFile
	 *            映射规则文件
	 * @throws IOException
	 * @throws DocumentException
	 * @throws TransformerException
	 */
	private void toJasper(File rptZipFile, File successDir, File outDir, boolean remainHierarchy, File styleFile)
			throws IOException, DocumentException, TransformerException {
		toJasper(rptZipFile, outDir, remainHierarchy, styleFile);
		// 转换成功之后，将文件移至其他路径下
		FileUtils.initDir(successDir);
		FileUtils.move(rptZipFile, new File(successDir, rptZipFile.getName()));
		System.out.println();
	}

	/**
	 * 单个rpt压缩文件转为jasper
	 * 
	 * @param rptZipFile
	 *            单个rpt压缩文件
	 * @param outDir
	 *            生成的jasper输出文件夹
	 * @param remainHierarchy
	 *            是否保持生成的jasper与rpt压缩路径层级相同
	 * @param styleFile
	 *            映射规则文件
	 * @throws IOException
	 * @throws DocumentException
	 * @throws TransformerException
	 */
	private void toJasper(File rptZipFile, File outDir, boolean remainHierarchy, File styleFile)
			throws IOException, DocumentException, TransformerException {
		logger.info("translating... " + rptZipFile.getName());
		FileUtils.initDir(outDir);
		String reportName = FileUtils.removeSuffix(rptZipFile.getName());
		File reportDir = remainHierarchy ? new File(outDir, reportName) : outDir;
		File uncompressedDir = new File(reportDir, "uncompressed");
		FileUtils.delete(uncompressedDir, false);
		FileUtils.initDir(uncompressedDir);

		// 先解压缩rpt压缩包
		ZipUtils.unzip(rptZipFile.getAbsolutePath(), uncompressedDir.getAbsolutePath());

		// 图片资源
		File picturesDir = new File(uncompressedDir, "Pictures");
		if (picturesDir.exists()) {
			File[] pictureFiles = picturesDir.listFiles();
			for (File pictureFile : pictureFiles) {
				File outPictureFile = new File(reportDir, pictureFile.getName());
				FileUtils.move(pictureFile, outPictureFile);
			}
		}

		// 获取解压后的所有子报表文件夹
		File[] subDirs = uncompressedDir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				if (file.isDirectory() && file.getName().startsWith("Sub_")) {
					return true;
				}
				return false;
			}
		});
		Map<Integer, String> subReportNames = new HashMap<>();
		for (File subDir : subDirs) {
			Integer subReportId = getSubReportId(subDir.getName());
			String subReportName = getSubReportName(subDir);
			subReportNames.put(subReportId, subReportName);
			File rptXmlFile = new File(subDir, "content.xml");
			File subReportFile = new File(reportDir, subReportName + ".jrxml");
			// 映射子报表为jrxml
			map(rptXmlFile, subReportFile, styleFile);
			// 修改报表名称
			modifyReportName(subReportFile, subReportName);
			// 处理查询语句
			processQueryString(rptXmlFile, subReportFile);
			// 移除模板中多余field
			removeUnusedFields(subReportFile);
			// 移除模板中多余变量
			removeUnusedVariables(subReportFile);
		}

		File rptXmlFile = new File(uncompressedDir, "content.xml");
		File reportFile = new File(reportDir, reportName + ".jrxml");
		// 映射主报表为jrxml
		map(rptXmlFile, reportFile, styleFile);
		// 修改报表名称
		modifyReportName(reportFile, reportName);
		// 处理查询语句
		processQueryString(rptXmlFile, reportFile);
		// 移除模板中多余field
		removeUnusedFields(reportFile);
		// 移除模板中多余变量
		removeUnusedVariables(reportFile);
		// 替换子报表路径
		processSubReportPath(reportFile, subReportNames);

		// 完成转换后删除解压缩的文件
		FileUtils.delete(uncompressedDir, false);
	}

	/**
	 * 根据映射文件将xml文件映射为jrxml文件
	 * 
	 * @param xmlFile
	 *            xml源文件
	 * @param outFile
	 *            输出的jrxml文件
	 * @param styleFile
	 *            映射文件
	 * @throws TransformerException
	 * @throws IOException
	 */
	private void map(File xmlFile, File outFile, File styleFile) throws TransformerException, IOException {
		String jrxml = MapHelper.map(xmlFile, styleFile);
		FileUtils.write(outFile, jrxml);
	}

	/**
	 * 获取子报表id
	 * 
	 * @param subDirName
	 *            子报表文件夹名称（如"Sub_1"）
	 * @return 子报表id
	 */
	private Integer getSubReportId(String subDirName) {
		return Integer.valueOf(subDirName.substring("Sub_".length()));
	}

	/**
	 * 获取子报表名称
	 * 
	 * @param subDir
	 *            子报表文件夹
	 * @return 子报表名称
	 * @throws DocumentException
	 */
	private String getSubReportName(File subDir) throws DocumentException {
		File metaXmlFile = new File(subDir, "meta.xml");
		Document document = new SAXReader().read(metaXmlFile);
		// XPath格式为：/*[name()='Meta']
		Node node = document.selectSingleNode("//*[name()='Title']");
		String text = node.getText();
		text = text.endsWith(".RPT") ? text.substring(0, text.length() - ".RPT".length()) : text;
		return text;
	}

	/**
	 * 修改报表名称
	 * 
	 * @param jrxmlFile
	 *            报表
	 * @param reportName
	 *            报表名称
	 * @throws DocumentException
	 * @throws IOException
	 */
	private void modifyReportName(File jrxmlFile, String reportName) throws DocumentException, IOException {
		Document document = new SAXReader().read(jrxmlFile);
		Element element = (Element) document.selectSingleNode("*[name()='jasperReport']");
		// 修改name节点
		Attribute attribute = element.attribute("name");
		attribute.setText(reportName);
		saveXmlFile(document, jrxmlFile);
	}

	/**
	 * 移除模板中多余field
	 * 
	 * @param jrxmlFile
	 *            报表
	 * @throws DocumentException
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	private void removeUnusedFields(File jrxmlFile) throws DocumentException, IOException {
		Set<String> usedFields = getUsedFields(jrxmlFile);
		if (!CollectionUtils.isEmpty(usedFields)) {
			Document document = new SAXReader().read(jrxmlFile);
			List<Element> elements = document.selectNodes("//*[name()='field']");
			List<String> fields = new ArrayList<>();
			for (Element element : elements) {
				String columnName = element.attributeValue("name");
				// field未实际使用，移除元素
				if (!usedFields.contains(columnName.toUpperCase())) {
					element.getParent().remove(element);
				}
				// 移除重复field
				else if (fields.contains(columnName)) {
					element.getParent().remove(element);
				} else {
					fields.add(columnName);
				}
			}
			saveXmlFile(document, jrxmlFile);
		}
	}

	/**
	 * 获取模板中实际使用的Field
	 * 
	 * @param jrxmlFilePath
	 * @return
	 * @throws IOException
	 */
	private Set<String> getUsedFields(File jrxmlFile) throws IOException {
		BufferedReader bufferedReader = new BufferedReader(new FileReader(jrxmlFile));
		String line = null;
		Set<String> result = new HashSet<>();
		while ((line = bufferedReader.readLine()) != null) {
			// 格式为$F{PD_TOTAL}
			Pattern pattern = Pattern.compile("\\$F\\{([^\\$]*)\\}");
			Matcher matcher = pattern.matcher(line);
			while (matcher.find()) {
				result.add(matcher.group(1).toUpperCase());
			}
		}
		bufferedReader.close();
		return result;
	}

	/**
	 * 移除模板中多余变量
	 * 
	 * @param jrxmlFile
	 *            报表
	 * @throws DocumentException
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	private void removeUnusedVariables(File jrxmlFile) throws DocumentException, IOException {
		Set<String> usedVariables = getUsedVariables(jrxmlFile);
		if (!CollectionUtils.isEmpty(usedVariables)) {
			Document document = new SAXReader().read(jrxmlFile);
			List<Element> elements = document.selectNodes("//*[name()='variable']");
			List<String> variableNames = new ArrayList<>();
			for (Element element : elements) {
				String variableName = element.attributeValue("name");
				// 变量未实际使用，移除元素
				if (!usedVariables.contains(variableName)) {
					element.getParent().remove(element);
				}
				// 移除重复variable
				else if (variableNames.contains(variableName)) {
					element.getParent().remove(element);
				} else {
					variableNames.add(variableName);
				}
			}
			saveXmlFile(document, jrxmlFile);
		}
	}

	/**
	 * 获取实际使用的变量
	 * 
	 * @param jrxmlFile
	 *            报表
	 * @return
	 * @throws IOException
	 */
	private Set<String> getUsedVariables(File jrxmlFile) throws IOException {
		BufferedReader bufferedReader = new BufferedReader(new FileReader(jrxmlFile));
		String line = null;
		Set<String> result = new HashSet<>();
		while ((line = bufferedReader.readLine()) != null) {
			// 格式为$V{PD_TOTAL}
			Pattern pattern = Pattern.compile("\\$V\\{([^\\$]*)\\}");
			Matcher matcher = pattern.matcher(line);
			while (matcher.find()) {
				result.add(matcher.group(1));
			}
		}
		bufferedReader.close();
		return result;
	}

	/**
	 * 处理报表中子报表的路径
	 * 
	 * @param jrxmlFile
	 *            报表
	 * @param subReportNames
	 *            子报表ID和名称
	 * @throws DocumentException
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	private void processSubReportPath(File jrxmlFile, Map<Integer, String> subReportNames)
			throws DocumentException, IOException {
		Document document = new SAXReader().read(jrxmlFile);
		List<Element> elements = document.selectNodes("//*[name()='subreport']");
		for (Element element : elements) {
			// id节点的值由xsl映射得到
			Node idNode = element.selectSingleNode("*[name()='id']");
			Integer subReportId = Integer.valueOf(idNode.getText());
			// 根据子报表id获取名称
			String subReportName = subReportNames.get(subReportId);
			if (StringUtils.isEmpty(subReportName)) {
				throw new IllegalStateException("子报表名称不存在：" + subReportId);
			}
			Node subreportExpressionNode = element.selectSingleNode("*[name()='subreportExpression']");
			// 修改subreportExpression节点
			subreportExpressionNode.setText("$P{REPORT_DIR} + \"/jrxml/" + subReportName + ".jasper\"");
			element.remove(idNode);
		}
		saveXmlFile(document, jrxmlFile);
	}

	/**
	 * 解析rpt的xml文件，获取sql查询语句
	 * 
	 * @param rptXmlFile
	 *            rpt的xml文件
	 * @param jrxmlFile
	 *            报表
	 * @return sql查询语句
	 * @throws DocumentException
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	private String parseQueryString(File rptXmlFile, File jrxmlFile) throws DocumentException, IOException {
		Set<String> usedFields = getUsedFields(jrxmlFile);
		Document document = new SAXReader().read(rptXmlFile);

		// 字段
		Map<String, List<String>> columnNames = new HashMap<>();

		// 表名
		// <Tablesource alias="FEEPLEASE"
		// databaseIdentifier="E_SHINE_DIGIT.FEEPLEASE">
		Map<String, String> tableNames = new HashMap<>();
		List<Element> tableSourceElements = document.selectNodes("//Tablesource");
		for (Element tableSourceElement : tableSourceElements) {
			String alias = tableSourceElement.attributeValue("alias");
			String text = tableSourceElement.attributeValue("databaseIdentifier");
			// 去除表名前的数据库名
			int index = text.lastIndexOf(".");
			String tableName = index < 0 ? text : text.substring(index + 1);
			tableNames.put(alias, tableName);

			// <Column name="FP_V13" type="11" />
			List<Element> columnElements = tableSourceElement.elements("Column");
			List<String> columns = new ArrayList<>();
			for (Element columnElement : columnElements) {
				String columnName = columnElement.attribute("name").getText();
				// 实际使用过的列才拼接到sql中
				if (usedFields.contains(columnName.toUpperCase())) {
					columns.add(columnName);
				}
			}
			columnNames.put(alias, columns);
		}

		// 表之间的join关系
		// <Join from="FEEPLEASE" to="FEEPLEASEDETAIL" type="loj">
		// <Link from="FP_ID" to="FPD_FPID" type="eq"/>
		// </Join>
		List<Join> joins = new ArrayList<>();
		List<Element> joinElements = document.selectNodes("//Join");
		for (Element joinElement : joinElements) {
			String fromTableAlias = joinElement.attribute("from").getText();
			String toTableAlias = joinElement.attribute("to").getText();
			String joinType = joinElement.attribute("type").getText();
			Join join = new Join(fromTableAlias, toTableAlias, getJoinType(joinType));

			List<Element> linkElements = joinElement.elements("Link");
			List<Link> links = new ArrayList<>();
			// 提取两个表join时，所连接的字段关系
			for (Element linkElement : linkElements) {
				String fromColumnName = linkElement.attribute("from").getText();
				String toColumnName = linkElement.attribute("to").getText();
				String linkType = linkElement.attribute("type").getText();
				Link link = new Link(fromColumnName, toColumnName, getLinkType(linkType));
				links.add(link);
			}
			join.setLinks(links);
			joins.add(join);
			join.setFromTableName(tableNames.get(fromTableAlias));
			join.setToTableName(tableNames.get(toTableAlias));
		}
		// 有join关系的表需从列表中排除，防止重复
		for (Join join : joins) {
			tableNames.remove(join.getFromTableAlias());
			tableNames.remove(join.getToTableAlias());
		}

		// 排序
		List<Sort> sorts = new ArrayList<>();
		// 组排序
		List<Element> groupFieldElements = document.selectNodes("//AreaPair[@type='GroupAreaPair']/AreaPairProperties");
		for (Element element : groupFieldElements) {
			String field = ((Element) element.selectSingleNode("Reference[@name='id']")).attributeValue("value");
			String sortType = "0";
			Element sortTypeElement = element.element("SortType");
			if (sortTypeElement != null) {
				sortType = sortTypeElement.attributeValue("value");
			}
			SortType sortType2 = getSortType(sortType);
			if (sortType2 != null) {
				Sort sort = new Sort(field, sortType2);
				sorts.add(sort);
			}
		}
		// 普通排序字段
		List<Element> sortElements = document.selectNodes("//SortFields/Field");
		for (Element element : sortElements) {
			String field = element.element("Name").attributeValue("value");
			String sortType = element.element("Operation").attributeValue("value");
			SortType sortType2 = getSortType(sortType);
			if (sortType2 != null) {
				Sort sort = new Sort(field, getSortType(sortType));
				sorts.add(sort);
			}
		}

		return getQueryString(columnNames, tableNames, joins, sorts);
	}

	private JoinType getJoinType(String type) {
		if ("inn".equalsIgnoreCase(type)) {
			return JoinType.INNER;
		} else if ("loj".equalsIgnoreCase(type)) {
			return JoinType.LEFT;
		} else if ("roj".equalsIgnoreCase(type)) {
			return JoinType.RIGHT;
		} else if ("foj".equalsIgnoreCase(type)) {
			return JoinType.FULL;
		} else {
			throw new IllegalArgumentException("无法解析join类型：" + type);
		}
	}

	private LinkType getLinkType(String type) {
		if ("eq".equalsIgnoreCase(type)) {
			return LinkType.EQ;
		} else if ("ne".equalsIgnoreCase(type)) {
			return LinkType.NE;
		} else if ("gt".equalsIgnoreCase(type)) {
			return LinkType.GT;
		} else if ("lt".equalsIgnoreCase(type)) {
			return LinkType.LT;
		} else if ("ge".equalsIgnoreCase(type)) {
			return LinkType.GTE;
		} else if ("le".equalsIgnoreCase(type)) {
			return LinkType.LTE;
		} else {
			throw new IllegalArgumentException("无法解析link类型：" + type);
		}
	}

	private SortType getSortType(String type) {
		if ("0".equalsIgnoreCase(type)) {
			return SortType.ASC;
		} else if ("1".equalsIgnoreCase(type)) {
			return SortType.DESC;
		} else {
			logger.error("无法解析sort类型：" + type);
			return null;
		}
	}

	/**
	 * 根据表名和连接关系获取查询语句
	 * 
	 * @param columnNames
	 *            列名
	 * @param tableNames
	 *            表名
	 * @param joins
	 *            连接关系
	 * @param sorts
	 *            排序
	 * @return
	 */
	private String getQueryString(Map<String, List<String>> columnNames, Map<String, String> tableNames,
			List<Join> joins, List<Sort> sorts) {
		StringBuilder builder = new StringBuilder("select");
		int i = 0;

		// 列名
		if (columnNames != null) {
			Set<Entry<String, List<String>>> entrySet = columnNames.entrySet();
			for (Entry<String, List<String>> entry : entrySet) {
				String tableName = entry.getKey();
				List<String> columns = entry.getValue();
				for (String columnName : columns) {
					if (i++ != 0) {
						builder.append(",");
					}
					// 列名前加表名，防止多个表含有相同字段
					builder.append(" ").append(tableName).append(".").append(columnName);
				}
			}
		}
		builder.append("\nfrom ");

		// 表名
		StringBuilder tableNameBuilder = new StringBuilder();
		i = 0;
		for (Entry<String, String> entry : tableNames.entrySet()) {
			if (i++ != 0) {
				tableNameBuilder.append(", ");
			}
			String alias = entry.getKey();
			String tableName = entry.getValue();
			tableNameBuilder.append(tableName);
			if (!alias.equals(tableName)) {
				tableNameBuilder.append(" ").append(alias);
			}
		}
		if (tableNameBuilder.length() > 0 && !CollectionUtils.isEmpty(joins)) {
			tableNameBuilder.append(", ");
		}
		// join
		for (Join join : joins) {
			String fromTableAlias = join.getFromTableAlias();
			String fromTableName = join.getFromTableName();
			if (!tableNameBuilder.toString().contains(fromTableName)) {
				tableNameBuilder.append(fromTableName);
				if (!fromTableAlias.equals(fromTableName)) {
					tableNameBuilder.append(" ").append(fromTableAlias);
				}
			}
			tableNameBuilder.append(" ").append(join.getExpression());
		}
		builder.append(tableNameBuilder);

		builder.append("\n$P!{WHERE_CONDITION}");

		// 排序
		i = 0;
		for (Sort sort : sorts) {
			if (i++ != 0) {
				builder.append(", ");
			} else {
				builder.append("\norder by ");
			}
			builder.append(sort.getExpression());
		}
		return builder.toString();
	}

	/**
	 * 修改queryString节点
	 * 
	 * @param rptXmlFile
	 *            rpt的xml文件
	 * @param jrxmlFile
	 *            报表
	 * @throws DocumentException
	 * @throws IOException
	 */
	private void processQueryString(File rptXmlFile, File jrxmlFile) throws DocumentException, IOException {
		Document document = new SAXReader().read(jrxmlFile);
		Node node = document.selectSingleNode("//*[name()='queryString']");
		String queryString = parseQueryString(rptXmlFile, jrxmlFile);
		node.setText(queryString);
		saveXmlFile(document, jrxmlFile);
	}

	/**
	 * 保存xml文件
	 * 
	 * @param document
	 * @param xmlFile
	 *            保存的xml文件
	 * @throws IOException
	 */
	private void saveXmlFile(Document document, File xmlFile) throws IOException {
		XMLWriter xmlWriter = new XMLWriter(new FileWriterWithEncoding(xmlFile, "UTF-8"));
		xmlWriter.write(document);
		xmlWriter.flush();
	}
}
