DocumentTools.java
package eu.javaexperience.document;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
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 org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import eu.javaexperience.interfaces.simple.getBy.GetBy1;
import eu.javaexperience.io.AppendableLocklessWriter;
import eu.javaexperience.reflect.Mirror;
public class DocumentTools
{
private DocumentTools() {}
public static final Node[] emptyNodeArrayInstance = new Node[0];
public static Document getOwnerDocument(Node node)
{
if (node.getNodeType() == Node.DOCUMENT_NODE)
{
return (Document)node;
}
else
{
return node.getOwnerDocument();
}
}
protected static final DocumentBuilder DEFAULT_BUILDER;
static
{
DocumentBuilder BUILDER = null;
try
{
BUILDER = DocumentBuilderFactory.newInstance().newDocumentBuilder();
}
catch(Exception e)
{
Mirror.propagateAnyway(e);
BUILDER = null;
}
DEFAULT_BUILDER = BUILDER;
}
public static Document createEmptyDocument()
{
return DEFAULT_BUILDER.newDocument();
}
public static void toString(Node node, Appendable app) throws TransformerException
{
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(node), new StreamResult(new AppendableLocklessWriter(app)));
}
public static String toString(Node node) throws TransformerException
{
StringBuilder sb = new StringBuilder();
toString(node, sb);
return sb.toString();
}
public static Document parseDocument(String doc) throws ParserConfigurationException, SAXException, IOException
{
return parseDocument(new ByteArrayInputStream(doc.getBytes()));
}
public static Document parseDocument(InputStream is) throws ParserConfigurationException, SAXException, IOException
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setSchema(null);
DocumentBuilder db = dbf.newDocumentBuilder();
return db.parse(is);
}
public static void selectAll(Node elem, Collection<Node> selected, GetBy1<Boolean, Node> selector)
{
if(Boolean.TRUE.equals(selector.getBy(elem)))
{
selected.add(elem);
}
recursiveSelect(elem.getChildNodes(), selected, selector);
}
public static void recursiveSelect(NodeList elements, Collection<Node> selected, GetBy1<Boolean, Node> selector)
{
int len = elements.getLength();
for(int i=0;i<len;++i)
{
Node e = elements.item(i);
selectAll(e, selected, selector);
}
}
public static Node findFirst(Node element, GetBy1<Boolean, Node> selector)
{
if(selector.getBy(element) == Boolean.TRUE)
{
return element;
}
NodeList nl = element.getChildNodes();
int size = nl.getLength();
for(int i=0;i<size;++i)
{
Node ret = findFirst(nl.item(i), selector);
if(null != ret)
{
return ret;
}
}
return null;
}
public static final GetBy1<Boolean, Node> selectAwithHREFattr = new GetBy1<Boolean, Node>()
{
@Override
public Boolean getBy(Node a)
{
return "a".equals(a.getNodeName()) && null != a.getAttributes() &&null != a.getAttributes().getNamedItem("href");
}
};
public static GetBy1<Boolean, Node> selectNodesByTagName(final String name)
{
return new GetBy1<Boolean, Node>()
{
@Override
public Boolean getBy(Node a)
{
return name.equals(a.getNodeName());
}
};
}
public static GetBy1<Boolean, Node> selectNodesByAttributeValue(final String attr, final String value)
{
return new GetBy1<Boolean, Node>()
{
@Override
public Boolean getBy(Node a)
{
NamedNodeMap nnm = a.getAttributes();
if(null == nnm)
{
return false;
}
Node n = nnm.getNamedItem(attr);
if(null == n)
{
return false;
}
return value.equals(n.getTextContent());
}
};
}
public static String txtOf(Node doc, GetBy1<Boolean, Node> selector)
{
if(null == doc)
{
return "";
}
ArrayList<Node> nds = new ArrayList<>();
selectAll(doc, nds, selector);
if(nds.isEmpty())
{
return "";
}
StringBuilder sb = new StringBuilder();
for(Node n:nds)
{
String s = n.getTextContent();
if(null != s)
{
sb.append(s);
}
}
return sb.toString();
}
public static String txtOf(Node node)
{
if(null == node)
{
return "";
}
return node.getTextContent();
}
public static Node getElementById(Node n, String id)
{
return findFirst(n, selectId(id));
}
public static GetBy1<Boolean, Node> selectId(String id)
{
return selectNodesByAttributeValue("id", id);
}
public static Node getAttr(Node n, String attr)
{
if(null == n || null == attr)
{
return null;
}
NamedNodeMap nnm = n.getAttributes();
if(null == nnm)
{
return null;
}
return nnm.getNamedItem(attr);
}
public static String getAttrString(Node node, String name)
{
Node n = getAttr(node, name);
if(null == n)
{
return null;
}
return n.getTextContent();
}
public static boolean hasAttribute(Node n, String attr)
{
return null != getAttr(n, attr);
}
public static boolean isAttribute(Node n, String attr, String value)
{
if(null == value)
{
return hasAttribute(n, attr);
}
Node re = getAttr(n, attr);
if(null == re)
{
return false;
}
return value.equals(re.getTextContent());
}
public static GetBy1<Boolean, Node> selectNodesByAttributeValueContains(final String attr, final String value)
{
return new GetBy1<Boolean, Node>()
{
@Override
public Boolean getBy(Node a)
{
a = getAttr(a, attr);
if(null == a)
{
return false;
}
String str = a.getTextContent();
if(null == str)
{
return false;
}
return str.indexOf(value) >= 0;
}
};
}
public static boolean isTagName(Node node, String name)
{
return node.getNodeName().equalsIgnoreCase(name);
}
public static Iterable<Node> iterableForChilds(Node n)
{
final NodeList nl = n.getChildNodes();
return new Iterable<Node>()
{
@Override
public Iterator<Node> iterator()
{
return new Iterator<Node>()
{
int i = 0;
@Override
public boolean hasNext()
{
return i < nl.getLength();
}
@Override
public Node next()
{
return nl.item(i++);
}
@Override
public void remove()
{
throw new IllegalAccessError("This iterator for Node's child is read only");
}
};
}
};
}
public static Node getOrCreateNamedNode(Document owner, NamedNodeMap map, String key)
{
Node ret = map.getNamedItem(key);
if(null == ret)
{
ret = owner.createAttribute(key);
map.setNamedItem(ret);
}
return ret;
}
public static Node copyAttrAndAdopChilds(Document doc, Node dst, Node src)
{
adoptAttributes(doc, dst, src);
NodeList nl = src.getChildNodes();
for(int i=0;i<nl.getLength();++i)
{
dst.appendChild(doc.adoptNode(nl.item(i).cloneNode(true)));
}
//interesting, but this works as expected (getting the soure node value but setting the destination with text content)
String val = src.getNodeValue();
if(null != val)
{
dst.setTextContent(val);
}
return dst;
}
public static void adoptAttributes(Document doc, Node dst, Node src)
{
NamedNodeMap dattr = dst.getAttributes();
NamedNodeMap attr = src.getAttributes();
if(null != attr)
{
for(int i=0;i < attr.getLength();++i)
{
Node a = attr.item(i);
getOrCreateNamedNode(doc, dattr, a.getNodeName()).setNodeValue(a.getNodeValue());
}
}
}
public static boolean isTextNode(Node node)
{
return Document.TEXT_NODE == node.getNodeType();
}
public static void toStringPretty(Node node, Appendable app) throws TransformerException
{
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute("indent-number", 4);
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(node), new StreamResult(new AppendableLocklessWriter(app)));
}
public static String toStringPretty(Node node) throws TransformerException
{
StringBuilder sb = new StringBuilder();
toStringPretty(node, sb);
return sb.toString();
}
}