![JAR search and dependency download from the Maven repository](/logo.png)
net.pincette.xml.Util Maven / Gradle / Ivy
package net.pincette.xml;
import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING;
import static net.pincette.util.MimeType.stripParameters;
import static net.pincette.util.Pair.pair;
import static net.pincette.util.StreamUtil.rangeExclusive;
import static net.pincette.util.StreamUtil.takeWhile;
import static net.pincette.util.Util.isUri;
import static net.pincette.util.Util.tryToDoRethrow;
import static net.pincette.util.Util.tryToGet;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
import net.pincette.util.Pair;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.helpers.AttributesImpl;
/** @author Werner Donn\u00e9 */
public class Util {
private Util() {}
/**
* Returns the stream of ancestors from the parent to the document element.
*
* @param node the given node.
* @return The stream.
*/
public static Stream ancestors(final Node node) {
return takeWhile(node.getParentNode(), Node::getParentNode, Element.class::isInstance)
.map(Element.class::cast);
}
public static Stream attributes(final Node node) {
return stream(node.getAttributes()).map(Attr.class::cast);
}
public static Stream children(final Node node) {
return stream(node.getChildNodes());
}
/**
* Returns the stream of nodes in document order starting from node
.
*
* @param node the given node.
* @return The stream.
*/
public static Stream documentOrder(final Node node) {
final UnaryOperator nextSibling =
n -> n.getNextSibling() != null ? n.getNextSibling() : findNextHigherSibling(n);
return takeWhile(
node,
n -> n.getFirstChild() != null ? n.getFirstChild() : nextSibling.apply(n),
Objects::nonNull);
}
private static Node findNextHigherSibling(final Node node) {
return node.getParentNode() != null
? Optional.ofNullable(node.getParentNode().getNextSibling())
.orElse(findNextHigherSibling(node.getParentNode()))
: null;
}
public static Optional> getIndex(
final AttributesImpl atts, final String namespaceUri, final String localName) {
return Optional.of(atts.getIndex(namespaceUri, localName))
.filter(i -> i != -1)
.map(i -> pair(i, atts));
}
@SuppressWarnings("squid:S3776") // Can't reduce complexity. It comes from the XML spec.
public static boolean isCombiningChar(final char c) {
return (c >= '\u0300' && c <= '\u0345')
|| (c >= '\u0360' && c <= '\u0361')
|| (c >= '\u0483' && c <= '\u0486')
|| (c >= '\u0591' && c <= '\u05a1')
|| (c >= '\u05a3' && c <= '\u05b9')
|| (c >= '\u05bb' && c <= '\u05bd')
|| c == '\u05bf'
|| (c >= '\u05c1' && c <= '\u05c2')
|| c == '\u05c4'
|| (c >= '\u064b' && c <= '\u0652')
|| c == '\u0670'
|| (c >= '\u06d6' && c <= '\u06dc')
|| (c >= '\u06dd' && c <= '\u06df')
|| (c >= '\u06e0' && c <= '\u06e4')
|| (c >= '\u06e7' && c <= '\u06e8')
|| (c >= '\u06ea' && c <= '\u06ed')
|| (c >= '\u0901' && c <= '\u0903')
|| c == '\u093c'
|| (c >= '\u093e' && c <= '\u094c')
|| c == '\u094d'
|| (c >= '\u0951' && c <= '\u0954')
|| (c >= '\u0962' && c <= '\u0963')
|| (c >= '\u0981' && c <= '\u0983')
|| c == '\u09bc'
|| c == '\u09be'
|| c == '\u09bf'
|| (c >= '\u09c0' && c <= '\u09c4')
|| (c >= '\u09c7' && c <= '\u09c8')
|| (c >= '\u09cb' && c <= '\u09cd')
|| c == '\u09d7'
|| (c >= '\u09e2' && c <= '\u09e3')
|| c == '\u0a02'
|| c == '\u0a3c'
|| c == '\u0a3e'
|| c == '\u0a3f'
|| (c >= '\u0a40' && c <= '\u0a42')
|| (c >= '\u0a47' && c <= '\u0a48')
|| (c >= '\u0a4b' && c <= '\u0a4d')
|| (c >= '\u0a70' && c <= '\u0a71')
|| (c >= '\u0a81' && c <= '\u0a83')
|| c == '\u0abc'
|| (c >= '\u0abe' && c <= '\u0ac5')
|| (c >= '\u0ac7' && c <= '\u0ac9')
|| (c >= '\u0acb' && c <= '\u0acd')
|| (c >= '\u0b01' && c <= '\u0b03')
|| c == '\u0b3c'
|| (c >= '\u0b3e' && c <= '\u0b43')
|| (c >= '\u0b47' && c <= '\u0b48')
|| (c >= '\u0b4b' && c <= '\u0b4d')
|| (c >= '\u0b56' && c <= '\u0b57')
|| (c >= '\u0b82' && c <= '\u0b83')
|| (c >= '\u0bbe' && c <= '\u0bc2')
|| (c >= '\u0bc6' && c <= '\u0bc8')
|| (c >= '\u0bca' && c <= '\u0bcd')
|| c == '\u0bd7'
|| (c >= '\u0c01' && c <= '\u0c03')
|| (c >= '\u0c3e' && c <= '\u0c44')
|| (c >= '\u0c46' && c <= '\u0c48')
|| (c >= '\u0c4a' && c <= '\u0c4d')
|| (c >= '\u0c55' && c <= '\u0c56')
|| (c >= '\u0c82' && c <= '\u0c83')
|| (c >= '\u0cbe' && c <= '\u0cc4')
|| (c >= '\u0cc6' && c <= '\u0cc8')
|| (c >= '\u0cca' && c <= '\u0ccd')
|| (c >= '\u0cd5' && c <= '\u0cd6')
|| (c >= '\u0d02' && c <= '\u0d03')
|| (c >= '\u0d3e' && c <= '\u0d43')
|| (c >= '\u0d46' && c <= '\u0d48')
|| (c >= '\u0d4a' && c <= '\u0d4d')
|| c == '\u0d57'
|| c == '\u0e31'
|| (c >= '\u0e34' && c <= '\u0e3a')
|| (c >= '\u0e47' && c <= '\u0e4e')
|| c == '\u0eb1'
|| (c >= '\u0eb4' && c <= '\u0eb9')
|| (c >= '\u0ebb' && c <= '\u0ebc')
|| (c >= '\u0ec8' && c <= '\u0ecd')
|| (c >= '\u0f18' && c <= '\u0f19')
|| c == '\u0f35'
|| c == '\u0f37'
|| c == '\u0f39'
|| c == '\u0f3e'
|| c == '\u0f3f'
|| (c >= '\u0f71' && c <= '\u0f84')
|| (c >= '\u0f86' && c <= '\u0f8b')
|| (c >= '\u0f90' && c <= '\u0f95')
|| c == '\u0f97'
|| (c >= '\u0f99' && c <= '\u0fad')
|| (c >= '\u0fb1' && c <= '\u0fb7')
|| c == '\u0fb9'
|| (c >= '\u20d0' && c <= '\u20dc')
|| c == '\u20e1'
|| (c >= '\u302a' && c <= '\u302f')
|| c == '\u3099'
|| c == '\u309a';
}
public static boolean isExtenderChar(final char c) {
return c == '\u00b7'
|| c == '\u02d0'
|| c == '\u02d1'
|| c == '\u0387'
|| c == '\u0640'
|| c == '\u0e46'
|| c == '\u0ec6'
|| c == '\u3005'
|| (c >= '\u3031' && c <= '\u3035')
|| (c >= '\u309d' && c <= '\u309e')
|| (c >= '\u30fc' && c <= '\u30fe');
}
public static boolean isNameChar(final char c) {
return Character.isLetterOrDigit(c)
|| c == '.'
|| c == '-'
|| c == '_'
|| c == ':'
|| isCombiningChar(c)
|| isExtenderChar(c);
}
public static boolean isName(String s) {
return s.length() > 0
&& isNameStartChar(s.charAt(0))
&& s.chars().allMatch(c -> isNameChar((char) c));
}
public static boolean isNameStartChar(final char c) {
return Character.isLetter(c) || c == '_' || c == ':';
}
public static boolean isXml(final String mimeType) {
return Optional.of(mimeType)
.map(m -> stripParameters(m).toLowerCase())
.map(m -> "text/xml".equals(m) || "application/xml".equals(m) || m.endsWith("+xml"))
.orElse(false);
}
public static boolean isXmlChar(final char c) {
return c == 0x9
|| c == 0xa
|| c == 0xd
|| (c >= 0x20 && c <= 0xd7ff)
|| (c >= 0xe000 && c <= 0xfffd);
}
public static boolean isXmlText(final String s) {
return s.chars().allMatch(c -> isXmlChar((char) c));
}
public static boolean isXmlText(char[] c) {
return isXmlText(new String(c));
}
/**
* Returns the stream of next siblings starting right after node
.
*
* @param node the given node.
* @return The stream.
*/
public static Stream nextSiblings(final Node node) {
return takeWhile(node.getNextSibling(), Node::getNextSibling, Objects::nonNull);
}
/**
* Returns the stream of previous siblings starting right before node
.
*
* @param node the given node.
* @return The stream.
*/
public static Stream previousSiblings(final Node node) {
return takeWhile(node.getPreviousSibling(), Node::getPreviousSibling, Objects::nonNull);
}
/**
* Utility for entity resolvers.
*
* @param baseURI the given base URI against which the resolution is done.
* @param systemId the given system identifier, which is resolved if it is relative.
* @return The resolved system identifier.
*/
public static String resolveSystemId(final String baseURI, final String systemId) {
final Supplier tryBaseURI =
() ->
baseURI.charAt(baseURI.length() - 1) == '/'
? (baseURI + systemId)
: (baseURI.substring(0, baseURI.lastIndexOf('/') + 1) + systemId);
final Supplier trySystemId =
() -> systemId.charAt(0) == '/' ? systemId : tryBaseURI.get();
return isUri(baseURI)
? tryToGet(() -> new URL(new URL(baseURI), systemId)).map(URL::toString).orElse(null)
: trySystemId.get();
}
/**
* Returns a factory with secure processing on.
*
* @return The factory.
*/
@SuppressWarnings("java:S2755") // The feature is set.
public static DocumentBuilderFactory secureDocumentBuilderFactory() {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
tryToDoRethrow(() -> factory.setFeature(FEATURE_SECURE_PROCESSING, true));
return factory;
}
/**
* Returns a factory with secure processing on.
*
* @return The factory.
*/
@SuppressWarnings("java:S2755") // The feature is set.
public static TransformerFactory secureTransformerFactory() {
final TransformerFactory factory = TransformerFactory.newInstance();
tryToDoRethrow(() -> factory.setFeature(FEATURE_SECURE_PROCESSING, true));
return factory;
}
public static Stream stream(final NamedNodeMap map) {
return rangeExclusive(0, map.getLength()).map(map::item);
}
public static Stream stream(final NodeList list) {
return rangeExclusive(0, list.getLength()).map(list::item);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy