uoa-validator-engine2/src/main/java/eu/dnetlib/validator2/engine/Helper.java

305 lines
11 KiB
Java

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<String> propertySupplier) {
return ensureNonEmpty(propertySupplier.get(), "Empty property: " + propertyName);
}
public static String readPropertyAndErrIfNotFound(String propertyName, Map<String, String> 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<? extends RuntimeException> 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 <T> String asString(T t) {
return t == null ? "<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 <T> boolean predicateSucceedsForAllNodes(NodeList nodes,
Function<Node, T> nodeReader,
Predicate<T> 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 <T> boolean predicateSucceedsForAtLeastOneNode(NodeList nodes,
Function<Node, T> nodeReader,
Predicate<T> 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 <T> boolean predicateFailsForAllNodes(NodeList nodes,
Function<Node, T> nodeReader,
Predicate<T> 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 <T> boolean predicateSucceedsForExactlyOneNode(NodeList nodes,
Function<Node, T> nodeReader,
Predicate<T> predicate) {
int len = nodes.getLength();
return len == 1 && predicate.test(nodeReader.apply(nodes.item(0)));
}
public static <T> NodeList nodesThatMatchThePredicate(NodeList nodes,
Function<Node, T> nodeReader,
Predicate<T> predicate) {
List<Node> 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<Integer> 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<String, Boolean> 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 <T, R extends Rule<T>> RuleDiagnostics<T, R> systemOut() {
return new PrintStreamDiagnostics<>(System.out);
}
public static <T, R extends Rule<T>> RuleDiagnostics<T, R> systemErr() {
return new PrintStreamDiagnostics<>(System.err);
}
}
private static class PrintStreamDiagnostics<T, R extends Rule<T>> implements RuleDiagnostics<T, R> {
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<Node> nodes;
private ListOfNodes(List<Node> 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<ZipEntry> filter,
BiConsumer<String, InputStream> 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<ZipEntry> ZIP_ENTRY_IS_FILE = zipEntry -> !zipEntry.isDirectory();
}