package eu.dnetlib.validator2.engine; import eu.dnetlib.validator2.validation.guideline.Builders; import eu.dnetlib.validator2.validation.guideline.Builders.ElementSpecBuilder; import eu.dnetlib.validator2.validation.guideline.Cardinality; import eu.dnetlib.validator2.validation.guideline.RequirementLevel; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class Helper { private static final String EMPTY = ""; public static boolean isEmpty(String s) { return (canonicalize(s).isEmpty()); } public static String ensurePropertyIsPresent(String propertyName, Supplier propertySupplier) { return ensureNonEmpty(propertySupplier.get(), "Empty property: " + propertyName); } public static String readPropertyAndErrIfNotFound(String propertyName, Map map) { return ensurePropertyIsPresent(propertyName, () -> map.get(propertyName)); } public static String ensureNonEmpty(String value, String errorMessage) { return ensureNonEmpty(value, () -> new RuntimeException(errorMessage)); } public static String ensureNonEmpty(String value, Supplier exceptionSupplier) { String canonical = canonicalize(value); if (canonical.isEmpty()) { throw exceptionSupplier.get(); } return canonical; } public static String canonicalize(String s) { if (s == null) return EMPTY; return s.trim(); } private static String asString(T t) { return t == null ? "" : t.toString(); } public static String stringify(Rule rule) { return rule.getClass().getSimpleName() + "[" + rule.getContext().getProperties(). stream(). map(prop -> prop.getName() + "=" + prop.getValue()). collect(Collectors.joining(",")) + "]"; } public static boolean predicateSucceedsForAllNodes(NodeList nodes, Function nodeReader, Predicate predicate) { int len = nodes.getLength(); if (len == 0) return false; // Is used in original implementation for (int i = 0; i < len; i++) { T t = nodeReader.apply(nodes.item(i)); if (!predicate.test(t)) return false; } return true; } public static boolean predicateSucceedsForAtLeastOneNode(NodeList nodes, Function nodeReader, Predicate predicate) { int len = nodes.getLength(); for (int i = 0; i < len; i++) { T t = nodeReader.apply(nodes.item(i)); if (predicate.test(t)) return true; } return false; } public static boolean predicateFailsForAllNodes(NodeList nodes, Function nodeReader, Predicate predicate) { int len = nodes.getLength(); for (int i = 0; i < len; i++) { T t = nodeReader.apply(nodes.item(i)); if (predicate.test(t)) return false; } return true; } public static boolean predicateSucceedsForExactlyOneNode(NodeList nodes, Function nodeReader, Predicate predicate) { int len = nodes.getLength(); return len == 1 && predicate.test(nodeReader.apply(nodes.item(0))); } public static NodeList nodesThatMatchThePredicate(NodeList nodes, Function nodeReader, Predicate predicate) { List filtered = new ArrayList<>(); if (nodes != null) { int len = nodes.getLength(); for (int i = 0; i < len; i++) { Node node = nodes.item(i); // System.out.println("Getting node: " + node); T t = nodeReader.apply(node); // System.out.println("Read node value: " + t); if (predicate.test(t)) { filtered.add(node); } } } // System.out.println(filtered.size() + " nodes matched the predicate"); return new ListOfNodes(filtered); } public static String getAttributeValue(Node node, String attrName) { if (node == null || !node.hasAttributes()) { return null; } Node attr = node.getAttributes().getNamedItem(attrName); if (attr == null) { return null; } return attr.getNodeValue(); } public static Predicate createCardinalityPredicate(long min, long max, boolean isInclusive) { return isInclusive ? (Integer len) -> min <= len && len <= max : (Integer len) -> min < len && len < max; } public static ElementSpecBuilder buildElement(String elementName, RequirementLevel requirementLevel, Cardinality cardinality) { if (requirementLevel == RequirementLevel.MANDATORY) { return Builders.forMandatoryElement(elementName, cardinality); } else if (requirementLevel == RequirementLevel.MANDATORY_IF_APPLICABLE) { Builders.forMandatoryIfApplicableElement(elementName, cardinality, null); } else if (requirementLevel == RequirementLevel.OPTIONAL) { if (cardinality == Cardinality.ONE_TO_N) { return Builders.forOptionalRepeatableElement(elementName); } else { return Builders.forOptionalElement(elementName); } } else if (requirementLevel == RequirementLevel.RECOMMENDED) { if (cardinality == Cardinality.ONE_TO_N) { return Builders.forRecommendedRepeatableElement(elementName); } else { return Builders.forRecommendedElement(elementName); } } throw new RuntimeException("Not reachable"); } public static ElementSpecBuilder addAttribute(ElementSpecBuilder builder, String attrName, RequirementLevel requirementLevel) { if (requirementLevel == RequirementLevel.MANDATORY) { return builder.withMandatoryAttribute(attrName); } else if (requirementLevel == RequirementLevel.OPTIONAL) { return builder.withOptionalAttribute(attrName); } else if (requirementLevel == RequirementLevel.RECOMMENDED) { return builder.withRecommendedAttribute(attrName); } throw new RuntimeException("Not reachable"); } public static class URLResolver { /* TODO In case of network error all subsequent evaluations of same url string will be false (incorrectly) */ private static final ConcurrentHashMap resolvedURLs = new ConcurrentHashMap<>(); public static boolean resolve(String URLString) { return resolvedURLs.computeIfAbsent(URLString, u -> { HttpURLConnection huc = null; try { URL url = new URL(u); huc = (HttpURLConnection) url.openConnection(); huc.setRequestMethod("HEAD"); return huc.getResponseCode() == HttpURLConnection.HTTP_OK; } catch (IOException e) { return false; } finally { if (huc != null) huc.disconnect(); } }); } } public static class Diagnostics { public static > RuleDiagnostics systemOut() { return new PrintStreamDiagnostics<>(System.out); } public static > RuleDiagnostics systemErr() { return new PrintStreamDiagnostics<>(System.err); } } private static class PrintStreamDiagnostics> implements RuleDiagnostics { private final PrintStream printStream; private PrintStreamDiagnostics(PrintStream printStream) { this.printStream = printStream; } @Override public void success(R rule, T t) { printStream.println("Success: value " + asString(t) + " passes the " + rule.getContext().getIdProperty().getValue()); } @Override public void failure(R rule, T t) { printStream.println("Failure: value " + asString(t) + " fails the " + rule.getContext().getIdProperty().getValue()); } @Override public void error(R rule, T t, Throwable err) { String trace = stackTraceOf(err); printStream.println( "Error: value " + asString(t) + " raised an error to the" + rule.getContext().getIdProperty().getValue() + ": " + trace ); } } private static class ListOfNodes implements NodeList { private final List nodes; private ListOfNodes(List nodes) { this.nodes = nodes; } @Override public Node item(int index) { return nodes.get(index); } @Override public int getLength() { return nodes.size(); } } private static String stackTraceOf(Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); return sw.toString(); } public static void forEachZipEntry(File zipFile, int bufferSize, Predicate filter, BiConsumer zipEntryConsumer) throws IOException { if (zipEntryConsumer == null) return; try (ZipInputStream zip = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry entry; while ((entry = zip.getNextEntry()) != null) { if (filter == null || filter.test(entry)) { ByteArrayOutputStream out = new ByteArrayOutputStream(bufferSize); byte[] bytesIn = new byte[bufferSize]; int read = 0; while((read = zip.read(bytesIn)) != -1) { out.write(bytesIn, 0, read); } zipEntryConsumer.accept(entry.getName(), new ByteArrayInputStream(out.toByteArray())); } zip.closeEntry(); } } } public static final Predicate ZIP_ENTRY_IS_FILE = zipEntry -> !zipEntry.isDirectory(); }