305 lines
11 KiB
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();
|
|
}
|