package com.vitco.util.xml;
import com.vitco.manager.error.ErrorHandlerInterface;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Encapsulates a xml file
*/
public class XmlFile {
private Document doc;
// get the direct node children of an element as NodeList
// that have a name equal to name
public static NodeList getDirectChildren(Element parent, String name)
{
final List<Node> list = new LinkedList<Node>();
NodeList nodeList = new NodeList() {
@Override
public Node item(int index) {
return list.get(index);
}
@Override
public int getLength() {
return list.size();
}
};
for(Node child = parent.getFirstChild(); child != null; child = child.getNextSibling())
{
if(child instanceof Element && name.equals(child.getNodeName())) {
list.add(child);
}
}
return nodeList;
}
// pattern
final Pattern datePatt = Pattern.compile("(.+?)(\\[)((\\-)?[0-9]+?)(\\])");
// current top node
private Element curTop;
// create root node as well
public XmlFile(String rootNode) {
try {
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
doc = docBuilder.newDocument();
doc.setXmlStandalone(true);
doc.appendChild(doc.createElement(rootNode));
resetTopNode();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
// set the current top element (all actions will be relative to this node!)
public void setTopNode(String path) {
curTop = _createPath(path);
}
// set the current top element (all actions will be relative to this node!)
public void resetTopNode(String path) {
resetTopNode();
curTop = _createPath(path);
}
// reset the current top element
public void resetTopNode() {
curTop = doc.getDocumentElement();
}
// internal - creates a path
private Element _createPath(String path) {
// split our array and loop over it
Element cur = curTop;
if (path.equals("")) {
return cur;
}
String[] pathArray = path.split("/");
for (String dir : pathArray) {
// find the identification
Integer pos = null;
String name = dir;
Matcher m = datePatt.matcher(dir);
if (m.matches()) {
pos = Integer.valueOf(m.group(3));
name = m.group(1);
}
NodeList list = getDirectChildren(cur, name);
int length = list.getLength();
if (pos == null) { // position not set
if (length > 0) { // exists (take the first)
cur = (Element) list.item(0);
} else { // doesn't exists (create)
Element newCur = doc.createElement(name);
cur.appendChild(newCur);
cur = newCur;
}
} else { // position is set
if (pos >= 0) { // position wants to select
if (length > pos) { // can select
cur = (Element) list.item(pos);
} else { // can not select
return null;
}
} else { // position is negative (wants to create!)
Element newCur = doc.createElement(name);
cur.appendChild(newCur);
cur = newCur;
}
}
}
return cur;
}
// go up one step
public boolean goUp() {
if (curTop != doc.getDocumentElement()) {
curTop = (Element) curTop.getParentNode();
return true;
} else {
return false;
}
}
// go up "level" steps
public boolean goUp(int level) {
int clevel = level;
boolean result = true;
while (clevel > 0 && result) {
result = goUp();
clevel--;
}
return result;
}
// delete the current node and go up
public boolean deleteChild(String child) {
boolean result = false;
// find the identification
Integer pos = null;
String name = child;
Matcher m = datePatt.matcher(child);
if (m.matches()) {
pos = Integer.valueOf(m.group(3));
name = m.group(1);
}
NodeList list = getDirectChildren(curTop, name);
int length = list.getLength();
if (pos == null) { // position not set
if (length > 0) { // exists (take the first)
curTop.removeChild(list.item(0));
result = true;
}
} else { // position is set
if (pos >= 0) { // position valid
if (length > pos) { // can select
curTop.removeChild(list.item(pos));
result = true;
}
}
}
return result;
}
// creates a path (public)
public boolean createPath(String path) {
return _createPath(path) == null;
}
// creates a path and adds text content
public boolean addTextContent(String path, String value) {
Element result = _createPath(path);
if (result == null) {
return false;
} else {
result.appendChild(doc.createTextNode(value));
return true;
}
}
// create a path and adds attributes
public boolean addAttributes(String path, String[] attrs) {
Element result = _createPath(path);
if (result == null) {
return false;
} else {
for (String attr : attrs) {
String[] toSet = attr.split("=", 2);
result.setAttribute(toSet[0], toSet[1]);
}
return true;
}
}
// create a path, adds attributes and text content
public boolean addAttrAndTextContent(String path, String[] attrs, String value) {
Element result = _createPath(path);
if (result == null) {
return false;
} else {
for (String attr : attrs) {
String[] toSet = attr.split("=", 2);
result.setAttribute(toSet[0], toSet[1]);
}
result.appendChild(doc.createTextNode(value));
return true;
}
}
// write this xml document as file(name)
public boolean writeToFile(String filename, ErrorHandlerInterface errorHandler) {
return writeToFile(new File(filename), errorHandler);
}
// write this xml document to file
public boolean writeToFile(File file, ErrorHandlerInterface errorHandler) {
boolean result = false;
try {
DOMSource domSource = new DOMSource(doc);
Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(domSource, new StreamResult(out));
out.close();
result = true;
} catch (FileNotFoundException e) {
errorHandler.handle(e);
} catch (TransformerException e) {
errorHandler.handle(e);
} catch (IOException e) {
errorHandler.handle(e);
}
return result;
}
}