package eu.dnetlib.validator2.engine.builtins; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import eu.dnetlib.validator2.engine.RuleEvaluationException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.xpath.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; public class XPathExpressionHelper { private static final Logger logger = LogManager.getLogger(); // XPath expression compilation private static final XPath XPATH = XPathFactory.newInstance().newXPath(); private static final ConcurrentHashMap COMPILED_EXPRESSIONS = new ConcurrentHashMap<>(); // XPath expression evaluation private static final int MAX_CACHE_ENTRIES = 100; // TODO: Make this configurable private static final LoadingCache> EVALUATED_EXPRESSIONS = CacheBuilder. newBuilder(). maximumSize(MAX_CACHE_ENTRIES). build(new CacheLoader>() { @Override public ConcurrentMap load(Document key) { return new ConcurrentHashMap<>(); } }); public static XPathExpression compile(String xpath) { return COMPILED_EXPRESSIONS.computeIfAbsent(xpath, s -> { try { logger.debug("Compiling {}", s); XPathExpression expr = XPATH.compile(s); logger.debug("Compiled {} = {}", s, expr); return expr; } catch (XPathExpressionException e) { logger.error("Compilation failure", e); throw new RuntimeException(e.getMessage(), e); } }); } public static NodeList nodeList(String xpath, Document doc) { logger.debug("Evaluating nodeList {}", xpath); return nodeList(compile(xpath), doc); } public static NodeList nodeList(XPathExpression expr, Document doc) { try { return EVALUATED_EXPRESSIONS.get(doc).computeIfAbsent(expr, (XPathExpression xpath) -> { try { NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); return nodes; } catch(XPathExpressionException ex) { throw new RuleEvaluationException(ex.getMessage(), ex); } }); } catch (ExecutionException e) { throw new RuleEvaluationException(e.getMessage(), e); } } public static String attr(String xpath, Node node) { logger.debug("Evaluating attr {}", xpath); return attr(compile(xpath), node); } public static String attr(XPathExpression expr, Node node) { try { String value = expr.evaluate(node); return value; } catch(XPathExpressionException ex) { throw new RuleEvaluationException(ex.getMessage(), ex); } } public static Node node(String xpath, Node node) { logger.debug("Evaluating node {}", xpath); return node(compile(xpath), node); } public static Node node(XPathExpression expr, Node node) { try { Node resultNode = (Node) expr.evaluate(node, XPathConstants.NODE); return resultNode; } catch(XPathExpressionException ex) { throw new RuleEvaluationException(ex.getMessage(), ex); } } }