package org.jabref.logic.util.io;
import java.io.StringWriter;
import java.util.AbstractList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.RandomAccess;
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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Currently used for debugging only
*/
public class XMLUtil {
private static final Log LOGGER = LogFactory.getLog(XMLUtil.class);
private XMLUtil() {
}
/**
* Prints out the document to standard out. Used to generate files for test cases.
*/
public static void printDocument(Document doc) {
try {
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(domSource, result);
System.out.println(writer);
} catch (TransformerException ex) {
LOGGER.error("", ex);
}
}
public static List<Node> asList(NodeList n) {
return n.getLength() == 0 ? Collections.emptyList() : new NodeListWrapper(n);
}
/**
* Gets the content of a subnode.
* For example,
* <item>
* <nodeName>content</nodeName>
* </item>
*/
public static Optional<String> getNodeContent(Node item, String nodeName) {
if (item.getNodeType() != Node.ELEMENT_NODE) {
return Optional.empty();
}
NodeList metadata = ((Element) item).getElementsByTagName(nodeName);
if (metadata.getLength() == 1) {
return Optional.ofNullable(metadata.item(0).getTextContent());
} else {
return Optional.empty();
}
}
/**
* Gets the content of an attribute.
* For example,
* <item attributeName="content" />
*/
public static Optional<String> getAttributeContent(Node item, String attributeName) {
NamedNodeMap attributes = item.getAttributes();
return Optional.ofNullable(attributes.getNamedItem(attributeName)).map(Node::getTextContent);
}
/**
* Gets a list of subnodes with the specified tag name.
* For example,
* <item>
* <node>first hit</node>
* <node>second hit</node>
* </item>
*/
public static List<Node> getNodesByName(Node item, String nodeName) {
if (item.getNodeType() != Node.ELEMENT_NODE) {
return Collections.emptyList();
}
NodeList nodes = ((Element) item).getElementsByTagName(nodeName);
return asList(nodes);
}
/**
* Gets a the first subnode with the specified tag name.
* For example,
* <item>
* <node>hit</node>
* <node>second hit, but not returned</node>
* </item>
*/
public static Optional<Node> getNode(Node item, String nodeName) {
return getNodesByName(item, nodeName).stream().findFirst();
}
// Wrapper to make NodeList iterable,
// taken from <a href="http://stackoverflow.com/questions/19589231/can-i-iterate-through-a-nodelist-using-for-each-in-java">StackOverflow Answer</a>.
private static final class NodeListWrapper extends AbstractList<Node> implements RandomAccess {
private final NodeList list;
NodeListWrapper(NodeList list) {
this.list = list;
}
@Override
public Node get(int index) {
return list.item(index);
}
@Override
public int size() {
return list.getLength();
}
}
}