initial commit
parent
d89a94b5d3
commit
959f81dc73
@ -0,0 +1,51 @@
|
||||
plugins {
|
||||
id 'groovy'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
group = 'eu.dnetlib'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
|
||||
repositories {
|
||||
|
||||
mavenCentral()
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'commons-io:commons-io:2.6',
|
||||
'com.google.guava:guava:29.0-jre',
|
||||
'org.apache.logging.log4j:log4j-api:2.13.3'
|
||||
testCompile 'org.apache.logging.log4j:log4j-core:2.13.3'
|
||||
testCompile 'org.spockframework:spock-core:1.3-groovy-2.5'
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
|
||||
jar(MavenPublication) {
|
||||
groupId = "$group"
|
||||
artifactId = "uoa-validator-engine2"
|
||||
version = "0.9.0"
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
systemProperties(System.getProperties())
|
||||
|
||||
minHeapSize = "256m"
|
||||
maxHeapSize = "4g"
|
||||
|
||||
testLogging {
|
||||
// Make sure output from
|
||||
// standard out or error is shown
|
||||
// in Gradle output.
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
Binary file not shown.
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
@ -0,0 +1,100 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -0,0 +1,127 @@
|
||||
DataFrame = (RecordId, TS, XMLRec)
|
||||
|
||||
LiteratureGuidelinesV3Profile profile = new LiteratureGuidelinesV3Profile();
|
||||
|
||||
spark.sql("Select * from DataFrame").map(
|
||||
String id = RecordId;
|
||||
Document doc = parse(XMLRec);
|
||||
profile.validate(id, doc);
|
||||
profile.guidelines().forEach() {
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
private static final ElementSpec TITLE_SPEC = Builders.
|
||||
forElement("datacite:title", RequirementLevel.MANDATORY, Cardinality.ONE_TO_N).
|
||||
//TODO: Add allowed values (IETF BCP 47, the IANA Language Subtag Registry)
|
||||
withAttribute("xml:lang", RequirementLevel.OPTIONAL, Cardinality.ZERO_TO_N).
|
||||
withAttribute("titleType", RequirementLevel.OPTIONAL, Cardinality.ZERO_TO_ONE,
|
||||
"AlternativeTitle", "Subtitle", "TranslatedTitle", "Other").
|
||||
build();
|
||||
|
||||
|
||||
|
||||
Builders.forElement("foo").optional(upperBound).recommended(upperBound).mandatory(lower, upper).
|
||||
mandatoryIfApplicable(lower, upper, XMLRule)
|
||||
|
||||
|
||||
// Context is always full path to element
|
||||
Builders.forContext("record", "metadata", "oai_dc:dc") // check for : to determine xpath "syntax"
|
||||
Builders.forElement("foo").valueMustStartWith("eu:info:///asdadf").allowedValues("one", "two" | Predicate);
|
||||
Builders.forElement("foo").valueMustMatchPredicate(Predicate<String>)...
|
||||
Builders.forElement("foo").valueMustNotMatchPredicate(Predicate<String>)...
|
||||
Builders.forElement("dc:type").firstOccurrence("...").other("...");
|
||||
Builders.forElement("foo").withCustomXpathValuePredicate("");
|
||||
Builders.forElement("foo").withFilter();
|
||||
Builders.forElement("foo", Req.Level.MANDATORY|REC|OPT|MandatoryIfApplicable(Predicate | Rule))
|
||||
Builders.forElement("foo", Req.LevelMandatoryIfApplicable).applicableWhen("...") (TITLE_SPEC).
|
||||
|
||||
# CRIS
|
||||
|
||||
## Spec builders
|
||||
|
||||
### The general approach
|
||||
|
||||
TYPE_SPEC = Builders.forElement("Type").mplah().mplah();
|
||||
FOO_SPEC = Builders.forElement("Foo").mplah().mplah();
|
||||
Builders.forElement("Publication").inContext("record", "metadata").
|
||||
withSubElement(TYPE_SPEC).
|
||||
withSubElement(FOO_SPEC).
|
||||
build();
|
||||
|
||||
### An element may contain "self-references"
|
||||
|
||||
Builders.ElementSpecBuilder PUBLISHED_IN_SPEC = Builders.
|
||||
forOptionalElement("PublishedIn").
|
||||
withSubElement(null); //TODO: Pass self
|
||||
|
||||
>> Introduce a "magic" self-reference method:
|
||||
Builders.ElementSpecBuilder PUBLISHED_IN_SPEC = Builders.
|
||||
forOptionalElement("PublishedIn").
|
||||
withSubElement(rootSpec()); // or selfSpec() or thisSpec()
|
||||
|
||||
|
||||
### An element may contain "supported classes" of sub-elements
|
||||
Builders.
|
||||
forOptionalRepeatableElement("References").
|
||||
//TODO: Optional 1 of 3 (Publication, Patent, Product)
|
||||
withSubElement(PUBLICATION_SPEC). //TODO: Pass proper spec
|
||||
withSubElement(null). //TODO: Pass self
|
||||
withSubElement(PRODUCT_SPEC); //TODO: Pass proper spec
|
||||
|
||||
>> Introduce a new withAllowedSubElements method, that accepts a list of element specs:
|
||||
Builders.
|
||||
forOptionalRepeatableElement("References").
|
||||
withAllowedSubElements(PUBLICATION_SPEC, rootSpec(), PRODUCT_SPEC);
|
||||
|
||||
|
||||
>> Note: We can unify the above with a "magic" ref method, e.g. ref(FOO_SPEC).
|
||||
|
||||
### Support namespaces
|
||||
For example:
|
||||
<Publication xmlns="https://www.openaire.eu/cerif-profile/1.1/" id="812348"><!-- Linking Data and Publications: Towards a Cross-Disciplinary Approach -->
|
||||
<Type xmlns="https://www.openaire.eu/cerif-profile/vocab/COAR_Publication_Types">http://purl.org/coar/resource_type/c_6501<!-- journal article --></Type>
|
||||
...
|
||||
</Publication>
|
||||
|
||||
>> TBD
|
||||
|
||||
## CRIS Profile Set / Family
|
||||
|
||||
class CRISProfileSet {
|
||||
boolean enableCrossChecking = false
|
||||
String baseURL = "oai:cris.example.org:"
|
||||
XMLApplicationProfile[]
|
||||
}
|
||||
|
||||
class NewXMLApplicationProfile {
|
||||
String baseURL = "oai:cris.example.org:"
|
||||
Resolver resolver = new Resolver(baseURL)
|
||||
validate(id, doc) {
|
||||
Rule
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd
|
||||
http://www.openarchives.org/OAI/2.0/ ">
|
||||
<responseDate>2020-04-01T09:31:11Z</responseDate>
|
||||
<request identifier="oai:http://helios-eie.ekt.gr:10442/8495" metadataPrefix="oai_dc" verb="GetRecord">
|
||||
http://pandektisint.ekt.gr:8080/EIE_oai/request
|
||||
</request>
|
||||
<GetRecord>
|
||||
<record>
|
||||
<header>
|
||||
<identifier>oai:http://helios-eie.ekt.gr:10442/8495</identifier>
|
||||
<datestamp>2016-01-19T12:55:45Z</datestamp>
|
||||
<setSpec>hdl_10442_62</setSpec>
|
||||
<setSpec>hdl_10442_11344</setSpec>
|
||||
<setSpec>ec_fundedresources</setSpec>
|
||||
<setSpec>driver</setSpec>
|
||||
<setSpec>video</setSpec>
|
||||
<setSpec>all</setSpec>
|
||||
</header>
|
||||
<metadata>
|
||||
<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
|
||||
<dc:coverage>Greece</dc:coverage>
|
||||
<dc:creator>Rizakis, Athanassios D.</dc:creator>
|
||||
<dc:date>1996</dc:date>
|
||||
<dc:title>Les colonies Romaines des côtes occidentales grecques. Population et territoires</dc:title>
|
||||
<dc:subject>LCC::D</dc:subject>
|
||||
<dc:subject>LCC::J::JA</dc:subject>
|
||||
<dc:subject>LCC::J::JC</dc:subject>
|
||||
<dc:subject>LCC::K</dc:subject>
|
||||
<dc:subject>LCC::H::HD1401</dc:subject>
|
||||
<dc:type>Text</dc:type>
|
||||
<dc:language>fra</dc:language>
|
||||
<dc:identifier>http://hdl.handle.net/10442/8495</dc:identifier>
|
||||
</oai_dc:dc>
|
||||
</metadata>
|
||||
</record>
|
||||
</GetRecord>
|
||||
</OAI-PMH>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?><OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"><responseDate>2020-04-01T09:19:04Z</responseDate><request verb="Identify">http://pandektisint.ekt.gr:8080/EIE_oai/request</request><Identify><repositoryName>${dspace.name}</repositoryName><baseURL>http://pandektisint.ekt.gr:8080/EIE_oai/request</baseURL><protocolVersion>2.0</protocolVersion><adminEmail>${mail.admin}</adminEmail><earliestDatestamp>2001-01-01T00:00:00Z</earliestDatestamp><deletedRecord>persistent</deletedRecord><granularity>YYYY-MM-DDThh:mm:ssZ</granularity><compression>gzip</compression><compression>deflate</compression><description><toolkit xsi:schemaLocation="http://oai.dlib.vt.edu/OAI/metadata/toolkit http://oai.dlib.vt.edu/OAI/metadata/toolkit.xsd" xmlns="http://oai.dlib.vt.edu/OAI/metadata/toolkit"><title>OCLC's OAICat Repository Framework</title><author><name>Jeffrey A. Young</name><email>jyoung@oclc.org</email><institution>OCLC</institution></author><version>1.5.48</version><toolkitIcon>http://alcme.oclc.org/oaicat/oaicat_icon.gif</toolkitIcon><URL>http://www.oclc.org/research/software/oai/cat.shtm</URL></toolkit></description></Identify></OAI-PMH>
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
|
||||
<responseDate>2020-04-01T09:13:16Z</responseDate>
|
||||
<request verb="GetRecord" metadataPrefix="oai_dc" identifier="oai:dlib.tuc.gr:21811">
|
||||
https://dias.library.tuc.gr/oaiHandler
|
||||
</request>
|
||||
<GetRecord>
|
||||
<record>
|
||||
<header>
|
||||
<identifier>oai:dlib.tuc.gr:21811_oa</identifier>
|
||||
<datestamp>2014-10-10T00:00:00Z</datestamp>
|
||||
<setSpec>28</setSpec>
|
||||
</header>
|
||||
<metadata>
|
||||
<oai_dc:dc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
|
||||
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
|
||||
<dc:type>info:eu-repo/semantics/article</dc:type>
|
||||
<dc:title xml:lang="en">Mainstream traffic flow control on freeways using variable speed limits
|
||||
</dc:title>
|
||||
<dc:creator xml:lang="en">Carlson Rodrigo Castelan ()</dc:creator>
|
||||
<dc:creator xml:lang="el">Παπαμιχαηλ Ιωαννης(http://users.isc.tuc.gr/~ipapa)</dc:creator>
|
||||
<dc:creator xml:lang="en">Papamichail Ioannis(http://users.isc.tuc.gr/~ipapa)</dc:creator>
|
||||
<dc:creator xml:lang="el">Παπαγεωργιου Μαρκος(http://users.isc.tuc.gr/~mpapageorgiou)</dc:creator>
|
||||
<dc:creator xml:lang="en">Papageorgiou Markos(http://users.isc.tuc.gr/~mpapageorgiou)</dc:creator>
|
||||
<dc:subject xml:lang="en">Traffic incident management,traffic congestion management,traffic incident
|
||||
management
|
||||
</dc:subject>
|
||||
<dc:identifier>http://purl.tuc.gr/dl/dias/CF2DBEC9-EA4E-4D15-931D-C8D5D903D718</dc:identifier>
|
||||
<dc:identifier>10.4237/transportes.v21i3.694</dc:identifier>
|
||||
<dc:date>Published at: 2014-09-25</dc:date>
|
||||
<dc:language>en</dc:language>
|
||||
<dc:rights>info:eu-repo/semantics/openAccess</dc:rights>
|
||||
<dc:rights xml:lang="en">License: http://creativecommons.org/licenses/by-nc-nd/4.0/</dc:rights>
|
||||
<dc:format>application/pdf</dc:format>
|
||||
<dc:date>Issued on: 2013</dc:date>
|
||||
<dc:description xml:lang="en">Summarization: Mainstream Traffic Flow Control (MTFC), enabled via
|
||||
variable speed limits, is a control concept for real-time freeway traffic management. The
|
||||
benefits of MTFC for efficient freeway traffic flow have been demonstrated recently using an
|
||||
optimal control approach and a feedback control approach. In this paper, both control approaches
|
||||
are reviewed and applied to a freeway network in a simulation environment. The validated network
|
||||
model used reflects an actual freeway (a ring-road), fed with actual (measured) demands. The
|
||||
optimal and feedback control results are discussed, compared and demonstrated to improve
|
||||
significantly the system performance. In particular, the feedback control scheme is deemed
|
||||
suitable for immediate practical application as it takes into account operational requirements
|
||||
and constraints, while its results are shown to be satisfactory. In addition, the control system
|
||||
performance was not very sensitive to variations of the parameters of the feedback controller.
|
||||
This result indicates that the burden associated with fine tuning of the controller may be
|
||||
reduced in the field.
|
||||
</dc:description>
|
||||
<dc:publisher xml:lang="en">ANPET - Associação Nacional de Pesquisa e Ensino em Transportes
|
||||
</dc:publisher>
|
||||
<dc:description xml:lang="en">Presented on: Transportes</dc:description>
|
||||
<dc:type>peer-reviewed</dc:type>
|
||||
<dc:identifier xml:lang="en">Bibliographic citation: R.C. Carlson, I. Papamichail, M. Papageorgiou,
|
||||
"Mainstream traffic flow control on freeways using variable speed limits," Transportes, vol. 21,
|
||||
no. 3, pp. 56-65, 2013.
|
||||
doi:10.4237/transportes.v21i3.694
|
||||
</dc:identifier>
|
||||
<dc:relation>info:eu-repo/grantAgreement/EC/FP7/246686</dc:relation>
|
||||
</oai_dc:dc>
|
||||
</metadata>
|
||||
</record>
|
||||
</GetRecord>
|
||||
</OAI-PMH>
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@
|
||||
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
|
||||
<responseDate>2020-04-01T09:06:59Z</responseDate>
|
||||
<request verb="Identify">https://dias.library.tuc.gr/oaiHandler</request>
|
||||
<Identify>
|
||||
<repositoryName>Technical University of Crete - Digital Library</repositoryName>
|
||||
<baseURL>https://dias.library.tuc.gr/oaiHandler</baseURL>
|
||||
<protocolVersion>2.0</protocolVersion>
|
||||
<adminEmail>pappas@isc.tuc.gr</adminEmail>
|
||||
<earliestDatestamp>2012-01-01T00:00:00Z</earliestDatestamp>
|
||||
<deletedRecord>persistent</deletedRecord>
|
||||
<granularity>YYYY-MM-DDThh:mm:ssZ</granularity>
|
||||
<compression>gzip</compression>
|
||||
<compression>deflate</compression>
|
||||
<description>
|
||||
<oai-identifier xmlns="http://www.openarchives.org/OAI/2.0/oai-identifier" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai-identifier http://www.openarchives.org/OAI/2.0/oai-identifier.xsd">
|
||||
<scheme>oai</scheme>
|
||||
<repositoryIdentifier>dlib.tuc.gr</repositoryIdentifier>
|
||||
<delimiter>:</delimiter>
|
||||
<sampleIdentifier>oai:dlib.tuc.gr:1002</sampleIdentifier>
|
||||
</oai-identifier>
|
||||
</description>
|
||||
<description>
|
||||
<toolkit xmlns="http://oai.dlib.vt.edu/OAI/metadata/toolkit" xsi:schemaLocation="http://oai.dlib.vt.edu/OAI/metadata/toolkit http://oai.dlib.vt.edu/OAI/metadata/toolkit.xsd">
|
||||
<title>OCLC's OAICat Repository Framework</title>
|
||||
<author>
|
||||
<name>Jeffrey A. Young</name>
|
||||
<email>jyoung@oclc.org</email>
|
||||
<institution>OCLC</institution>
|
||||
</author>
|
||||
<version>1.5.48</version>
|
||||
<toolkitIcon>http://alcme.oclc.org/oaicat/oaicat_icon.gif</toolkitIcon>
|
||||
<URL>http://www.oclc.org/research/software/oai/cat.shtm</URL>
|
||||
</toolkit>
|
||||
</description>
|
||||
</Identify>
|
||||
</OAI-PMH>
|
@ -0,0 +1,85 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSSerializer;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
public class CrisClass {
|
||||
|
||||
private String classId;
|
||||
private String ClassSchemeId;
|
||||
private String startDate;
|
||||
private String endDate;
|
||||
|
||||
// Maybe will change to constructor calling the private default constructor. Will see
|
||||
public static CrisClass getInstance(Node node) throws XPathExpressionException, ParserConfigurationException {
|
||||
CrisClass instance = new CrisClass();
|
||||
|
||||
Document newXMLDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
|
||||
Element root = newXMLDocument.createElement("root");
|
||||
newXMLDocument.appendChild(root);
|
||||
Node copyNode = newXMLDocument.importNode(node, true);
|
||||
root.appendChild(copyNode);
|
||||
printXMLDocument(newXMLDocument);
|
||||
|
||||
XPathFactory factory = XPathFactory.newInstance();
|
||||
XPath xPath = factory.newXPath();
|
||||
|
||||
instance.setClassId((String) xPath.evaluate("//cfClassId/text()", newXMLDocument, XPathConstants.STRING));
|
||||
instance.setClassSchemeId((String) xPath.evaluate("//cfClassSchemeId/text()", newXMLDocument, XPathConstants.STRING));
|
||||
instance.setStartDate((String) xPath.evaluate("//cfStartDate/text()", newXMLDocument, XPathConstants.STRING));
|
||||
instance.setEndDate((String) xPath.evaluate("//cfEndDate/text()", newXMLDocument, XPathConstants.STRING));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Not sure of the effect of this method. Implemented as in the original source.
|
||||
private static void printXMLDocument(Document document) {
|
||||
DOMImplementationLS domImplementationLS = (DOMImplementationLS) document.getImplementation();
|
||||
LSSerializer lsSerializer = domImplementationLS.createLSSerializer();
|
||||
lsSerializer.writeToString(document);
|
||||
}
|
||||
|
||||
private CrisClass() {}
|
||||
|
||||
public String getClassId() {
|
||||
return classId;
|
||||
}
|
||||
|
||||
public void setClassId(String classId) {
|
||||
this.classId = classId;
|
||||
}
|
||||
|
||||
public String getClassSchemeId() {
|
||||
return ClassSchemeId;
|
||||
}
|
||||
|
||||
public void setClassSchemeId(String classSchemeId) {
|
||||
ClassSchemeId = classSchemeId;
|
||||
}
|
||||
|
||||
public String getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(String startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public String getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(String endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
}
|
@ -0,0 +1,304 @@
|
||||
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();
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Predicates {
|
||||
|
||||
// The following are not used
|
||||
/*
|
||||
public static <T> Predicate<T> and(final Iterable<Predicate<T>> predicates) {
|
||||
return t -> {
|
||||
for(Predicate<T> predicate: predicates) {
|
||||
if (!predicate.test(t)) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> or(final Iterable<Predicate<T>> predicates) {
|
||||
return t -> {
|
||||
for(Predicate<T> predicate: predicates) {
|
||||
if (predicate.test(t)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// horn?
|
||||
public static <T> Predicate<T> conditional(Predicate<T> condition, final Iterable<Predicate<T>> predicates) {
|
||||
return t -> {
|
||||
if (!condition.test(t)) return true;
|
||||
return and(predicates).test(t);
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
public static class SetOfCaseInsensitiveAllowedValues implements Predicate<String> {
|
||||
|
||||
private final Set<String> allowedValues;
|
||||
|
||||
public SetOfCaseInsensitiveAllowedValues(String[] allowedValues) {
|
||||
this.allowedValues = Stream.of(allowedValues).
|
||||
filter(Objects::nonNull).
|
||||
map(String::trim).
|
||||
filter(term -> !term.isEmpty()).
|
||||
map(String::toLowerCase). // we want to ignore case for equality
|
||||
collect(Collectors.toCollection(HashSet::new));;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
String textToTest = Helper.canonicalize(s);
|
||||
if (textToTest.isEmpty()) return false;
|
||||
return allowedValues.contains(textToTest.toLowerCase()); // we want to ignore case for equality
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return allowedValues.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> all(Iterable<Rule<T>> rules) {
|
||||
return t -> {
|
||||
for(Predicate<T> predicate: rules) {
|
||||
if (!predicate.test(t)) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> any(final Iterable<Rule<T>> rules) {
|
||||
return t -> {
|
||||
for(Predicate<T> predicate: rules) {
|
||||
if (predicate.test(t)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> not(Rule<T> rule) {
|
||||
return t -> !rule.test(t);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface PropertyDriven {
|
||||
|
||||
void readFrom(Map<String, String> map);
|
||||
|
||||
void writeTo(Map<String, String> map);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
public class Reporter<T, R extends Rule<T>> {
|
||||
|
||||
private final RuleDiagnostics<T, R> diagnostics;
|
||||
|
||||
public Reporter(RuleDiagnostics<T, R> diagnostics) {
|
||||
this.diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
public void reportSuccessFor(R rule, T t) {
|
||||
try {
|
||||
diagnostics.success(rule, t);
|
||||
}
|
||||
catch(Throwable throwable) {
|
||||
System.err.println("Failed to report success of applying " + rule + " to value: " + t);
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void reportFailureFor(R rule, T t) {
|
||||
try {
|
||||
diagnostics.failure(rule, t);
|
||||
}
|
||||
catch(Throwable throwable) {
|
||||
System.err.println("Failed to report failure of applying " + rule + " to value: " + t);
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void reportErrorFor(R rule, T t, Throwable throwable) {
|
||||
try {
|
||||
diagnostics.error(rule, t, throwable);
|
||||
}
|
||||
catch(Throwable throwable1) {
|
||||
System.err.println("Failed to report error of applying " + rule + " to value: " + t);
|
||||
throwable1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface Rule<T> extends Predicate<T> {
|
||||
|
||||
<C extends RuleContext> C getContext();
|
||||
|
||||
@Override
|
||||
boolean test(T t) throws RuleEvaluationException;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface RuleBuilder<R extends Rule> {
|
||||
|
||||
R build();
|
||||
|
||||
R buildFrom(Map<String, String> map);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public interface RuleContext extends PropertyDriven {
|
||||
|
||||
String ID_PROPERTY_NAME = "id";
|
||||
|
||||
RuleProperty getIdProperty();
|
||||
|
||||
Collection<RuleProperty> getProperties();
|
||||
|
||||
@Override
|
||||
default void readFrom(final Map<String, String> map) {
|
||||
getProperties().forEach(prop -> {
|
||||
prop.readFrom(map);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
default void writeTo(final Map<String, String> map) {
|
||||
getProperties().forEach(prop -> {
|
||||
prop.writeTo(map);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
public interface RuleDiagnostics<T, R extends Rule<T>> {
|
||||
|
||||
void success(R rule, T t);
|
||||
|
||||
void failure(R rule, T t);
|
||||
|
||||
void error(R rule, T t, Throwable err);
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
public class RuleEngine {
|
||||
|
||||
public static <T, R extends Rule<T>> void applyAndReport(R rule, T t, Reporter<T, R> reporter) {
|
||||
|
||||
if (reporter == null) throw new IllegalArgumentException("Reporter cannot be null");
|
||||
if (rule == null) throw new IllegalArgumentException("Rule cannot be null");
|
||||
|
||||
try {
|
||||
if(rule.test(t)) {
|
||||
reporter.reportSuccessFor(rule, t);
|
||||
}
|
||||
else {
|
||||
reporter.reportFailureFor(rule, t);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
reporter.reportErrorFor(rule, t, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
public class RuleEvaluationException extends RuntimeException {
|
||||
|
||||
public RuleEvaluationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public RuleEvaluationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
public interface RuleProperty extends PropertyDriven {
|
||||
|
||||
String getName();
|
||||
|
||||
String getValue();
|
||||
|
||||
void setValue(String value);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package eu.dnetlib.validator2.engine;
|
||||
|
||||
public enum Status {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
ERROR
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.*;
|
||||
|
||||
abstract class AbstractRuleBuilder<R extends Rule, C extends RuleContext> implements RuleBuilder<R> {
|
||||
|
||||
C context;
|
||||
|
||||
AbstractRuleBuilder(C context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
void ensureContextIsValid() {
|
||||
for (RuleProperty prop : context.getProperties()) {
|
||||
if (Helper.isEmpty(prop.getValue())) {
|
||||
throw new IllegalStateException("Empty value for property " + prop.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
|
||||
public class AlwaysErrRule<T> extends SimpleRule<T, SimpleContext> {
|
||||
public AlwaysErrRule() {
|
||||
super(new SimpleContext("Err"), (T t) -> { throw new RuleEvaluationException(null); });
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
public class AlwaysFailRule<T> extends SimpleRule<T, SimpleContext> {
|
||||
public AlwaysFailRule() {
|
||||
super(new SimpleContext("fail"), (T t) -> false);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
public class AlwaysSucceedRule<T> extends SimpleRule<T, SimpleContext> {
|
||||
|
||||
public AlwaysSucceedRule() {
|
||||
super(new SimpleContext("succeed"), (T t) -> true);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Predicates;
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
|
||||
public class AndRule<T, C extends RuleContext> extends SimpleRule<T, C> {
|
||||
|
||||
public AndRule(C ctx, Iterable<Rule<T>> rules) {
|
||||
super(ctx, Predicates.all(rules));
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
|
||||
public class ConditionalRule<T> extends ForwardingRule<T> {
|
||||
|
||||
public enum WhenConditionFails {
|
||||
RULE_FAILS,
|
||||
RULE_SUCCEEDS
|
||||
}
|
||||
|
||||
protected final Rule<T> conditionRule;
|
||||
protected final WhenConditionFails whenConditionFails;
|
||||
|
||||
public ConditionalRule(Rule<T> actualRule, Rule<T> conditionRule, WhenConditionFails whenConditionFails) {
|
||||
super(actualRule);
|
||||
this.conditionRule = conditionRule;
|
||||
this.whenConditionFails = whenConditionFails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) throws RuleEvaluationException {
|
||||
if (conditionRule.test(t)) {
|
||||
return super.test(t);
|
||||
}
|
||||
else {
|
||||
return whenConditionFails == WhenConditionFails.RULE_SUCCEEDS;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
|
||||
public class ForwardingRule<T> implements Rule<T> {
|
||||
|
||||
protected final Rule<T> rule;
|
||||
|
||||
public ForwardingRule(Rule<T> rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <C extends RuleContext> C getContext() {
|
||||
return rule.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) throws RuleEvaluationException {
|
||||
return rule.test(t);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
public class SimpleContext extends StandardRuleContext {
|
||||
|
||||
public SimpleContext(String valueOfIdProperty) {
|
||||
getIdProperty().setValue(valueOfIdProperty);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SimpleRule<T, C extends RuleContext> implements Rule<T> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private final C context;
|
||||
private final Predicate<T> predicate;
|
||||
|
||||
public SimpleRule(C context, Predicate<T> predicate) {
|
||||
this.context = context;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) throws RuleEvaluationException {
|
||||
try {
|
||||
logger.debug("Applying {}", context.getIdProperty().getValue());
|
||||
return predicate.test(t);
|
||||
} catch (Throwable throwable) {
|
||||
// Catch all exceptions here to simplify predicate code
|
||||
throw new RuleEvaluationException(throwable.getMessage(), throwable);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.contexts.BooleanRuleProperty;
|
||||
|
||||
class StandardBooleanRuleProperty extends StandardRuleProperty implements BooleanRuleProperty {
|
||||
|
||||
private boolean isTrue;
|
||||
|
||||
StandardBooleanRuleProperty(String name) {
|
||||
super(name);
|
||||
super.setValue(Boolean.toString(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue() {
|
||||
return isTrue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrue(boolean isTrue) {
|
||||
this.isTrue = isTrue;
|
||||
super.setValue(Boolean.toString(isTrue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
try {
|
||||
super.setValue(value);
|
||||
isTrue = Boolean.parseBoolean(getValue());
|
||||
} catch (RuntimeException re) {
|
||||
throw new IllegalArgumentException("Not a boolean for property " + getName());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.CrisClassSchemeContext;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLCrisClassSchemeContextWithVocabulary;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
class StandardCrisClassXMLContextWithVocabulary
|
||||
extends StandardXMLContextWithVocabulary
|
||||
implements XMLCrisClassSchemeContextWithVocabulary {
|
||||
|
||||
private final StandardRuleProperty schemeId = new StandardRuleProperty(CrisClassSchemeContext.ID_PROPERTY_NAME);
|
||||
|
||||
@Override
|
||||
public StandardRuleProperty getCrisClassSchemeIdProperty() {
|
||||
return schemeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
Collection<RuleProperty> props = new ArrayList<>(super.getProperties());
|
||||
props.add(schemeId);
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.contexts.FieldsProperty;
|
||||
|
||||
class StandardFieldsProperty extends StandardRuleProperty implements FieldsProperty {
|
||||
|
||||
private String[] xpaths;
|
||||
|
||||
public StandardFieldsProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public String[] getXpaths() {
|
||||
return xpaths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
super.setValue(value);
|
||||
xpaths = getValue().split(",");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.contexts.LongRuleProperty;
|
||||
|
||||
public class StandardLongRuleProperty extends StandardRuleProperty implements LongRuleProperty {
|
||||
|
||||
private long longValue;
|
||||
|
||||
StandardLongRuleProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLongValue() {
|
||||
return longValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLongValue(long value) {
|
||||
this.longValue = value;
|
||||
super.setValue(String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
try {
|
||||
super.setValue(value);
|
||||
longValue = Long.parseLong(getValue());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Not a valid number for property " + getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.contexts.NodeListAction;
|
||||
import eu.dnetlib.validator2.engine.contexts.NodeListActionProperty;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class StandardNodeListActionProperty extends StandardRuleProperty implements NodeListActionProperty {
|
||||
|
||||
private static final Map<String, NamedNodeListAction> SUPPORTED_ACTIONS = new HashMap<>();
|
||||
|
||||
static {
|
||||
Arrays.asList(new CUSTOM(), new ALL(), new ANY(), new NONE(), new ONE(), new LENGTH()).forEach(action ->
|
||||
SUPPORTED_ACTIONS.put(action.name, action)
|
||||
);
|
||||
}
|
||||
|
||||
private NamedNodeListAction action;
|
||||
|
||||
StandardNodeListActionProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return action == null ? null : action.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
NamedNodeListAction action = SUPPORTED_ACTIONS.get(value);
|
||||
if (action == null) throw new IllegalArgumentException("Invalid node list action: " + value);
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> predicate) throws IllegalStateException {
|
||||
if (action == null) throw new IllegalStateException("Node list action property is empty");
|
||||
return action.test(nodes, predicate);
|
||||
}
|
||||
|
||||
private static abstract class NamedNodeListAction implements NodeListAction {
|
||||
final String name;
|
||||
NamedNodeListAction(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CUSTOM extends NamedNodeListAction {
|
||||
|
||||
CUSTOM() { super("custom"); }
|
||||
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> predicate) throws IllegalStateException {
|
||||
throw new IllegalStateException("Configuration error: a custom node list action was expected");
|
||||
}
|
||||
}
|
||||
|
||||
private static class ALL extends NamedNodeListAction {
|
||||
|
||||
ALL() {
|
||||
super("all");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> appliesToAllNodes) throws IllegalStateException {
|
||||
return Helper.predicateSucceedsForAllNodes(
|
||||
nodes,
|
||||
Node::getNodeValue,
|
||||
(Predicate<String>)appliesToAllNodes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ANY extends NamedNodeListAction {
|
||||
|
||||
ANY() {
|
||||
super(">0");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> applyToAtLeastOne) throws IllegalStateException {
|
||||
return Helper.predicateSucceedsForAtLeastOneNode(
|
||||
nodes,
|
||||
Node::getNodeValue,
|
||||
(Predicate<String>)applyToAtLeastOne
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NONE extends NamedNodeListAction { // Success 0
|
||||
|
||||
NONE() {
|
||||
super("0");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> applyToNone) throws IllegalStateException {
|
||||
return Helper.predicateFailsForAllNodes(
|
||||
nodes,
|
||||
Node::getNodeValue,
|
||||
(Predicate<String>)applyToNone
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ONE extends NamedNodeListAction { // Success 1
|
||||
|
||||
ONE() {
|
||||
super("1");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> applyToExactlyOne) throws IllegalStateException {
|
||||
return Helper.predicateSucceedsForExactlyOneNode(
|
||||
nodes,
|
||||
Node::getNodeValue,
|
||||
(Predicate<String>) applyToExactlyOne
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LENGTH extends NamedNodeListAction {
|
||||
LENGTH() {
|
||||
super("length");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean test(NodeList nodes, Predicate<?> lengthInRange) throws IllegalStateException {
|
||||
return lengthOfNodesMatches(nodes, (Predicate<Integer>)lengthInRange);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean lengthOfNodesMatches(NodeList nodes, Predicate<Integer> predicate) {
|
||||
return predicate.test(nodes.getLength());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.contexts.RegularExpressionProperty;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
class StandardRegularExpressionProperty extends StandardRuleProperty implements RegularExpressionProperty {
|
||||
|
||||
private static final ConcurrentHashMap<String, Pattern> compiledPatterns = new ConcurrentHashMap<>();
|
||||
|
||||
public StandardRegularExpressionProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String text) throws IllegalStateException, PatternSyntaxException {
|
||||
String expression = getValue();
|
||||
if (Helper.isEmpty(expression)) {
|
||||
throw new IllegalStateException("Empty regular expression for property " + getName());
|
||||
}
|
||||
|
||||
Pattern pattern = compiledPatterns.computeIfAbsent(expression, Pattern::compile);
|
||||
return pattern.matcher(text).matches();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class StandardRuleContext implements RuleContext {
|
||||
|
||||
private final StandardRuleProperty id =
|
||||
new StandardRuleProperty(RuleContext.ID_PROPERTY_NAME);
|
||||
|
||||
@Override
|
||||
public StandardRuleProperty getIdProperty() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
return Arrays.asList(id);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleDiagnostics;
|
||||
import eu.dnetlib.validator2.engine.Status;
|
||||
|
||||
public class StandardRuleDiagnostics<T, R extends Rule<T>> implements RuleDiagnostics<T, R> {
|
||||
|
||||
private Status lastReportedStatus;
|
||||
private R lastReportedRule;
|
||||
private T lastReportedValue;
|
||||
private Throwable lastReportedError;
|
||||
|
||||
@Override
|
||||
public void success(R rule, T t) {
|
||||
setReported(Status.SUCCESS, rule, t, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(R rule, T t) {
|
||||
setReported(Status.FAILURE, rule, t, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(R rule, T t, Throwable err) {
|
||||
setReported(Status.ERROR, rule, t, err);
|
||||
}
|
||||
|
||||
private void setReported(Status status, R rule, T value, Throwable error) {
|
||||
this.lastReportedStatus = status;
|
||||
this.lastReportedRule = rule;
|
||||
this.lastReportedValue = value;
|
||||
this.lastReportedError = error;
|
||||
}
|
||||
|
||||
public Status getLastReportedStatus() {
|
||||
return lastReportedStatus;
|
||||
}
|
||||
|
||||
public Rule<T> getLastReportedRule() {
|
||||
return lastReportedRule;
|
||||
}
|
||||
|
||||
public T getLastReportedValue() {
|
||||
return lastReportedValue;
|
||||
}
|
||||
|
||||
public Throwable getLastReportedError() {
|
||||
return lastReportedError;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class StandardRuleProperty implements RuleProperty {
|
||||
|
||||
private final String name;
|
||||
private String value;
|
||||
|
||||
public StandardRuleProperty(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(Map<String, String> map) {
|
||||
/*
|
||||
TODO Condition has been added to (hopefully) handle properly:
|
||||
- optional properties (e.g. termsType in XMLVocabularyRule)
|
||||
- private properties / not exposed to or set by the user (e.g. nodeListAction = "length" in XMLCardinalityRule)
|
||||
These could/would not be provided from user map
|
||||
*/
|
||||
if (map.containsKey(getName())) {
|
||||
setValue(map.get(getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(Map<String, String> map) {
|
||||
map.put(name, getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
String newValue = Helper.canonicalize(value);
|
||||
if (newValue.isEmpty()) throw new IllegalArgumentException("Empty value for property " + name);
|
||||
this.value = newValue;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Predicates;
|
||||
import eu.dnetlib.validator2.engine.contexts.TermsProperty;
|
||||
|
||||
class StandardTermsProperty extends StandardRuleProperty implements TermsProperty {
|
||||
|
||||
private Predicates.SetOfCaseInsensitiveAllowedValues terms;
|
||||
|
||||
public StandardTermsProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
super.setValue(value);
|
||||
terms = new Predicates.SetOfCaseInsensitiveAllowedValues(getValue().split(","));
|
||||
if (terms.isEmpty()) throw new IllegalArgumentException("Empty value for terms property.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean termExists(String text) {
|
||||
return terms.test(text);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class StandardXMLContext extends StandardRuleContext implements XMLContext {
|
||||
|
||||
private final StandardXPathExpressionProperty xpath =
|
||||
new StandardXPathExpressionProperty(XPathExpressionContext.PROPERTY_NAME);
|
||||
private final StandardNodeListActionProperty nodeListAction =
|
||||
new StandardNodeListActionProperty(NodeListContext.PROPERTY_NAME);
|
||||
|
||||
|
||||
@Override
|
||||
public NodeListActionProperty getNodeListActionProperty() {
|
||||
return nodeListAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XPathExpressionProperty getXPathExpressionProperty() {
|
||||
return xpath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
Collection<RuleProperty> props = new ArrayList<>(super.getProperties());
|
||||
props.add(xpath);
|
||||
props.add(nodeListAction);
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.BooleanRuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.CardinalityContext;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithCardinality;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class StandardXMLContextWithCardinality
|
||||
extends StandardXMLContext
|
||||
implements XMLContextWithCardinality {
|
||||
|
||||
private final StandardLongRuleProperty lowerBound =
|
||||
new StandardLongRuleProperty(CardinalityContext.LOWER_BOUND_PROPERTY_NAME);
|
||||
private final StandardLongRuleProperty upperBound =
|
||||
new StandardLongRuleProperty(CardinalityContext.UPPER_BOUND_PROPERTY_NAME);
|
||||
private final StandardBooleanRuleProperty isInclusive =
|
||||
new StandardBooleanRuleProperty(CardinalityContext.IS_INCLUSIVE_PROPERTY_NAME);
|
||||
|
||||
@Override
|
||||
public StandardLongRuleProperty getLowerBoundProperty() {
|
||||
return lowerBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardLongRuleProperty getUpperBoundProperty() {
|
||||
return upperBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanRuleProperty getIsInclusiveProperty() {
|
||||
return isInclusive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
Collection<RuleProperty> props = new ArrayList<>(super.getProperties());
|
||||
props.add(lowerBound);
|
||||
props.add(upperBound);
|
||||
props.add(isInclusive);
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.FieldsProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.NotConfusedFieldsContext;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithNotConfusedFields;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
class StandardXMLContextWithNotConfusedFields
|
||||
extends StandardXMLContext
|
||||
implements XMLContextWithNotConfusedFields {
|
||||
|
||||
private final StandardFieldsProperty fields = new StandardFieldsProperty(NotConfusedFieldsContext.PROPERTY_NAME);
|
||||
|
||||
@Override
|
||||
public FieldsProperty getFieldsProperty() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
Collection<RuleProperty> props = new ArrayList<>(super.getProperties());
|
||||
props.add(fields);
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.RegularExpressionContext;
|
||||
import eu.dnetlib.validator2.engine.contexts.RegularExpressionProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithRegularExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
class StandardXMLContextWithRegularExpression
|
||||
extends StandardXMLContext
|
||||
implements XMLContextWithRegularExpression {
|
||||
|
||||
private final StandardRegularExpressionProperty regexp =
|
||||
new StandardRegularExpressionProperty(RegularExpressionContext.PROPERTY_NAME);
|
||||
|
||||
@Override
|
||||
public RegularExpressionProperty getRegularExpressionProperty() {
|
||||
return regexp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
Collection<RuleProperty> props = new ArrayList<>(super.getProperties());
|
||||
props.add(regexp);
|
||||
return props;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.VocabularyContext;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithVocabulary;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
class StandardXMLContextWithVocabulary
|
||||
extends StandardXMLContext
|
||||
implements XMLContextWithVocabulary {
|
||||
|
||||
private final StandardTermsProperty terms = new StandardTermsProperty(VocabularyContext.TERMS_PROPERTY_NAME);
|
||||
private final StandardRuleProperty termsType = new StandardRuleProperty(VocabularyContext.TERMS_TYPE_PROPERTY_NAME);
|
||||
|
||||
@Override
|
||||
public StandardTermsProperty getTermsProperty() {
|
||||
return terms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardRuleProperty getTermsTypeProperty() {
|
||||
return termsType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RuleProperty> getProperties() {
|
||||
Collection<RuleProperty> props = new ArrayList<>(super.getProperties());
|
||||
props.add(terms);
|
||||
props.add(termsType);
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.contexts.XPathExpressionProperty;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
|
||||
public class StandardXPathExpressionProperty extends StandardRuleProperty implements XPathExpressionProperty {
|
||||
|
||||
public StandardXPathExpressionProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) throws IllegalArgumentException {
|
||||
XPathExpressionHelper.compile(Helper.canonicalize(value));
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
public NodeList evaluate(Document doc) {
|
||||
String xpath = Helper.canonicalize(getValue());
|
||||
return XPathExpressionHelper.nodeList(xpath, doc);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithCardinality;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class XMLCardinalityRule extends XMLRule<XMLContextWithCardinality> {
|
||||
|
||||
protected XMLCardinalityRule(XMLContextWithCardinality context) {
|
||||
super(context, (NodeList nodes) ->
|
||||
context.getNodeListActionProperty().test(nodes, context.cardinalityPredicate()));
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLCardinalityRule, XMLContextWithCardinality> {
|
||||
|
||||
private Builder() {
|
||||
super(new StandardXMLContextWithCardinality());
|
||||
context.getNodeListActionProperty().setValue("length");
|
||||
}
|
||||
|
||||
public Builder setId(String id) throws IllegalArgumentException {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setXPathExpression(String xpath) throws IllegalArgumentException {
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRange(String greaterThan, String lessThan) throws IllegalArgumentException {
|
||||
context.getLowerBoundProperty().setValue(greaterThan);
|
||||
context.getUpperBoundProperty().setValue(lessThan);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRange(long greaterThan, long lessThan) {
|
||||
context.getLowerBoundProperty().setLongValue(greaterThan);
|
||||
context.getUpperBoundProperty().setLongValue(lessThan);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setIsInclusive(boolean isInclusive) {
|
||||
context.getIsInclusiveProperty().setTrue(isInclusive);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLCardinalityRule build() throws IllegalStateException {
|
||||
ensureContextIsValid();
|
||||
return new XMLCardinalityRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLCardinalityRule buildFrom(Map<String, String> map) throws IllegalStateException {
|
||||
context.readFrom(map);
|
||||
ensureContextIsValid();
|
||||
return new XMLCardinalityRule(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import eu.dnetlib.validator2.engine.contexts.TermsProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLCrisClassSchemeContextWithVocabulary;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class XMLCrisClassVocabularyRule extends XMLRule<XMLCrisClassSchemeContextWithVocabulary> {
|
||||
|
||||
protected XMLCrisClassVocabularyRule(XMLCrisClassSchemeContextWithVocabulary context) {
|
||||
super(context, (NodeList nodes) -> {
|
||||
String schemeId = context.getCrisClassSchemeIdProperty().getValue();
|
||||
TermsProperty terms = context.getTermsProperty();
|
||||
|
||||
/*
|
||||
TODO Implement test code
|
||||
Following original implementation, NodeListAction is applied to select nodes of NodeList
|
||||
*/
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLCrisClassVocabularyRule, XMLCrisClassSchemeContextWithVocabulary> {
|
||||
|
||||
Builder() {
|
||||
super(new StandardCrisClassXMLContextWithVocabulary());
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
// Terms Type Property of VocabularyContext is not used here...
|
||||
context.getTermsTypeProperty().setValue("notNull");
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setXPathExpression(String xpath) {
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNodeListAction(String nodeListAction) throws RuntimeException {
|
||||
context.getNodeListActionProperty().setValue(nodeListAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVocabularyTerms(String terms) {
|
||||
context.getTermsProperty().setValue(terms);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCrisClassSchemeId(String id) {
|
||||
context.getCrisClassSchemeIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLCrisClassVocabularyRule build() {
|
||||
ensureContextIsValid();
|
||||
return new XMLCrisClassVocabularyRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLCrisClassVocabularyRule buildFrom(Map<String, String> map) {
|
||||
context.readFrom(map);
|
||||
ensureContextIsValid();
|
||||
return new XMLCrisClassVocabularyRule(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContext;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class XMLFieldExistsRule extends XMLRule<XMLContext> {
|
||||
|
||||
protected XMLFieldExistsRule(XMLContext context) {
|
||||
super(context, (NodeList nodes) -> {
|
||||
/*
|
||||
TODO
|
||||
- if (nodes.getLength() == 0) return false;
|
||||
- Is in original implementation, but results in incorrect evaluation if traversal=0
|
||||
*/
|
||||
return context.getNodeListActionProperty().test(nodes, (String v) -> !Helper.isEmpty(v));
|
||||
});
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLFieldExistsRule, XMLContext> {
|
||||
|
||||
private Builder() {
|
||||
super(new StandardXMLContext()); //the actual implementation is known by the builder only
|
||||
}
|
||||
|
||||
public Builder setId(String id) throws IllegalArgumentException {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setXPathExpression(String xpath) throws IllegalArgumentException {
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNodeListAction(String nodeListAction) throws RuntimeException {
|
||||
context.getNodeListActionProperty().setValue(nodeListAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLFieldExistsRule build() {
|
||||
ensureContextIsValid();
|
||||
return new XMLFieldExistsRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLFieldExistsRule buildFrom(Map<String, String> map) {
|
||||
context.readFrom(map);
|
||||
ensureContextIsValid();
|
||||
return new XMLFieldExistsRule(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithNotConfusedFields;
|
||||
import eu.dnetlib.validator2.engine.contexts.XPathExpressionProperty;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class XMLNotConfusedFieldsRule implements Rule<Document> {
|
||||
|
||||
private final XMLContextWithNotConfusedFields context;
|
||||
|
||||
protected XMLNotConfusedFieldsRule(XMLContextWithNotConfusedFields context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuleContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override //TODO Is a bit different from original evaluation. Needs revisit probably.
|
||||
public boolean test(Document doc) throws RuleEvaluationException {
|
||||
XPathExpressionProperty xpathExprProp = context.getXPathExpressionProperty();
|
||||
Set<String> allNodeValues = new HashSet<>();
|
||||
try {
|
||||
for (String xpath : context.getFieldsProperty().getXpaths()) {
|
||||
xpathExprProp.setValue(xpath);
|
||||
NodeList nodesReturned = xpathExprProp.evaluate(doc);
|
||||
boolean nodesReturnedAreUnique = context.getNodeListActionProperty().test(nodesReturned, (String v) -> {
|
||||
String val = v.trim().toLowerCase();
|
||||
return allNodeValues.add(val);
|
||||
});
|
||||
if (!nodesReturnedAreUnique) return false;
|
||||
}
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
throw new RuleEvaluationException(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLNotConfusedFieldsRule, XMLContextWithNotConfusedFields> {
|
||||
|
||||
Builder() {
|
||||
super(new StandardXMLContextWithNotConfusedFields());
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
context.getXPathExpressionProperty().setValue("notNull");
|
||||
context.getNodeListActionProperty().setValue("all");
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFields(String fields) {
|
||||
context.getFieldsProperty().setValue(fields);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLNotConfusedFieldsRule build() {
|
||||
ensureContextIsValid();
|
||||
return new XMLNotConfusedFieldsRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLNotConfusedFieldsRule buildFrom(Map<String, String> map) {
|
||||
context.readFrom(map);
|
||||
ensureContextIsValid();
|
||||
return new XMLNotConfusedFieldsRule(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import eu.dnetlib.validator2.engine.contexts.RegularExpressionProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithRegularExpression;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class XMLRegularExpressionRule extends XMLRule<XMLContextWithRegularExpression> {
|
||||
|
||||
protected XMLRegularExpressionRule(XMLContextWithRegularExpression context) {
|
||||
super(context, (NodeList nodes) -> {
|
||||
RegularExpressionProperty regex = context.getRegularExpressionProperty();
|
||||
return context.getNodeListActionProperty().test(nodes, (Predicate<String>) regex::matches);
|
||||
});
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLRegularExpressionRule, XMLContextWithRegularExpression> {
|
||||
|
||||
private Builder() {
|
||||
super(new StandardXMLContextWithRegularExpression());
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setXPathExpression(String xpath) {
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegularExpression(String regexp) {
|
||||
context.getRegularExpressionProperty().setValue(regexp);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNodeListAction(String nodeListAction) throws RuntimeException {
|
||||
context.getNodeListActionProperty().setValue(nodeListAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMLRegularExpressionRule build() {
|
||||
ensureContextIsValid();
|
||||
return new XMLRegularExpressionRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLRegularExpressionRule buildFrom(Map<String, String> map) {
|
||||
context.readFrom(map);
|
||||
ensureContextIsValid();
|
||||
return new XMLRegularExpressionRule(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* An XML rule that evaluates an xpath in an XMLContext and then injects a custom nodelist predicate to
|
||||
* carry out the actual rule application.
|
||||
*/
|
||||
public class XMLRule<C extends XMLContext> implements Rule<Document> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private final C context;
|
||||
public final Predicate<NodeList> predicate;
|
||||
|
||||
public XMLRule(C context, Predicate<NodeList> predicate) {
|
||||
this.context = context;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Document doc) throws RuleEvaluationException {
|
||||
try {
|
||||
logger.debug("Applying {}", context.getIdProperty().getValue());
|
||||
NodeList nodes = context.getXPathExpressionProperty().evaluate(doc);
|
||||
return predicate.test(nodes);
|
||||
} catch (Throwable t) {
|
||||
// Catch all exceptions here to simplify predicate code
|
||||
throw new RuleEvaluationException(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getContext().getIdProperty().getValue();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContext;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class XMLValidURLRule extends XMLRule<XMLContext> {
|
||||
|
||||
protected XMLValidURLRule(XMLContext context) {
|
||||
super(context, (NodeList nodes) -> {
|
||||
return context.getNodeListActionProperty().test(nodes, (Predicate<String>) Helper.URLResolver::resolve);
|
||||
});
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLValidURLRule, XMLContext> {
|
||||
|
||||
private Builder() {
|
||||
super(new StandardXMLContext());
|
||||
}
|
||||
|
||||
public Builder setId(String id) throws IllegalArgumentException {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setXPathExpression(String xpath) throws IllegalArgumentException {
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNodeListAction(String nodeListAction) throws RuntimeException {
|
||||
context.getNodeListActionProperty().setValue(nodeListAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLValidURLRule build() {
|
||||
ensureContextIsValid();
|
||||
return new XMLValidURLRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLValidURLRule buildFrom(Map<String, String> map) {
|
||||
context.readFrom(map);
|
||||
ensureContextIsValid();
|
||||
return new XMLValidURLRule(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package eu.dnetlib.validator2.engine.builtins;
|
||||
|
||||
import eu.dnetlib.validator2.engine.contexts.TermsProperty;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContextWithVocabulary;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class XMLVocabularyRule extends XMLRule<XMLContextWithVocabulary> {
|
||||
|
||||
protected XMLVocabularyRule(XMLContextWithVocabulary context) {
|
||||
super(context, (NodeList nodes) -> {
|
||||
TermsProperty terms = context.getTermsProperty();
|
||||
boolean result = context.getNodeListActionProperty().test(nodes, (Predicate<String>) terms::termExists);
|
||||
|
||||
String termsType = context.getTermsTypeProperty().getValue().toLowerCase();
|
||||
|
||||
//TODO: Check original implementation for blacklist/whitelist. It looks weird.
|
||||
if (termsType.equals("blacklist")) return !result;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder extends AbstractRuleBuilder<XMLVocabularyRule, XMLContextWithVocabulary> {
|
||||
|
||||
private Builder() {
|
||||
super(new StandardXMLContextWithVocabulary());
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
context.getIdProperty().setValue(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setXPathExpression(String xpath) {
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNodeListAction(String nodeListAction) throws RuntimeException {
|
||||
context.getNodeListActionProperty().setValue(nodeListAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVocabularyTerms(String terms) {
|
||||
context.getTermsProperty().setValue(terms);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVocabularyTermsAndTermsType(String terms, String termsType) {
|
||||
context.getTermsProperty().setValue(terms);
|
||||
context.getTermsTypeProperty().setValue(termsType);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initializeOptionalTermsTypeProperty() {
|
||||
if (context.getTermsTypeProperty().getValue() == null) {
|
||||
context.getTermsTypeProperty().setValue("blacklist");
|
||||
}
|
||||
}
|
||||
|
||||
public XMLVocabularyRule build() {
|
||||
initializeOptionalTermsTypeProperty();
|
||||
ensureContextIsValid();
|
||||
return new XMLVocabularyRule(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XMLVocabularyRule buildFrom(Map<String, String> map) {
|
||||
context.readFrom(map);
|
||||
initializeOptionalTermsTypeProperty();
|
||||
ensureContextIsValid();
|
||||
return new XMLVocabularyRule(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
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<String, XPathExpression> COMPILED_EXPRESSIONS = new ConcurrentHashMap<>();
|
||||
|
||||
// XPath expression evaluation
|
||||
private static final int MAX_CACHE_ENTRIES = 100; // TODO: Make this configurable
|
||||
private static final LoadingCache<Document, ConcurrentMap<XPathExpression, NodeList>>
|
||||
EVALUATED_EXPRESSIONS =
|
||||
CacheBuilder.
|
||||
newBuilder().
|
||||
maximumSize(MAX_CACHE_ENTRIES).
|
||||
build(new CacheLoader<Document, ConcurrentMap<XPathExpression, NodeList>>() {
|
||||
@Override
|
||||
public ConcurrentMap<XPathExpression, NodeList> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface BooleanRuleProperty extends RuleProperty {
|
||||
|
||||
boolean isTrue();
|
||||
|
||||
void setTrue(boolean isTrue);
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface CardinalityContext extends RuleContext {
|
||||
|
||||
String GREATER_THAN_PROPERTY_NAME = "gt";
|
||||
String LESS_THAN_PROPERTY_NAME = "lt";
|
||||
String UPPER_BOUND_PROPERTY_NAME = LESS_THAN_PROPERTY_NAME;
|
||||
String LOWER_BOUND_PROPERTY_NAME = GREATER_THAN_PROPERTY_NAME;
|
||||
String IS_INCLUSIVE_PROPERTY_NAME = "inclusive";
|
||||
|
||||
LongRuleProperty getLowerBoundProperty();
|
||||
|
||||
LongRuleProperty getUpperBoundProperty();
|
||||
|
||||
BooleanRuleProperty getIsInclusiveProperty();
|
||||
|
||||
default Predicate<Integer> cardinalityPredicate() {
|
||||
// System.out.println("Evaluating cardinality " + getIdProperty().getValue());
|
||||
long min = getLowerBoundProperty().getLongValue();
|
||||
long max = getUpperBoundProperty().getLongValue();
|
||||
|
||||
// TODO: [Minor] Perhaps we should disallow changing the properties
|
||||
// of a rule's context after the rule has been created?
|
||||
return Helper.createCardinalityPredicate(min, max, getIsInclusiveProperty().isTrue());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface CrisClassSchemeContext extends RuleContext {
|
||||
|
||||
String ID_PROPERTY_NAME = "scheme_id";
|
||||
|
||||
RuleProperty getCrisClassSchemeIdProperty();
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface FieldsProperty extends RuleProperty {
|
||||
|
||||
String[] getXpaths();
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface LongRuleProperty extends RuleProperty {
|
||||
|
||||
long getLongValue();
|
||||
|
||||
void setLongValue(long value);
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface NodeListAction {
|
||||
boolean test(NodeList nodes, Predicate<?> predicate) throws IllegalStateException;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface NodeListActionProperty extends RuleProperty, NodeListAction {
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
|
||||
public interface NodeListContext extends RuleContext {
|
||||
|
||||
String PROPERTY_NAME = "xmlTraversal";
|
||||
|
||||
NodeListActionProperty getNodeListActionProperty();
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
|
||||
public interface NotConfusedFieldsContext extends RuleContext {
|
||||
|
||||
String PROPERTY_NAME = "fields";
|
||||
|
||||
FieldsProperty getFieldsProperty();
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
|
||||
public interface RegularExpressionContext extends RuleContext {
|
||||
|
||||
String PROPERTY_NAME = "regexp";
|
||||
|
||||
RegularExpressionProperty getRegularExpressionProperty();
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
public interface RegularExpressionProperty extends RuleProperty {
|
||||
|
||||
boolean matches(String text) throws IllegalStateException, PatternSyntaxException;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface TermsProperty extends RuleProperty {
|
||||
|
||||
boolean termExists(String term);
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
|
||||
public interface VocabularyContext extends RuleContext {
|
||||
|
||||
String TERMS_PROPERTY_NAME = "terms";
|
||||
String TERMS_TYPE_PROPERTY_NAME = "terms_type";
|
||||
|
||||
TermsProperty getTermsProperty();
|
||||
|
||||
RuleProperty getTermsTypeProperty();
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
|
||||
public interface XMLContext extends RuleContext, XPathExpressionContext, NodeListContext {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
public interface XMLContextWithCardinality extends XMLContext, CardinalityContext {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
public interface XMLContextWithNotConfusedFields extends XMLContext, NotConfusedFieldsContext {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
public interface XMLContextWithRegularExpression extends XMLContext, RegularExpressionContext {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
public interface XMLContextWithVocabulary extends XMLContext, VocabularyContext {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
public interface XMLCrisClassSchemeContextWithVocabulary extends XMLContextWithVocabulary, CrisClassSchemeContext {
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
|
||||
public interface XPathExpressionContext extends RuleContext {
|
||||
|
||||
String PROPERTY_NAME = "xpath";
|
||||
|
||||
XPathExpressionProperty getXPathExpressionProperty();
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package eu.dnetlib.validator2.engine.contexts;
|
||||
|
||||
import eu.dnetlib.validator2.engine.RuleProperty;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
public interface XPathExpressionProperty extends RuleProperty {
|
||||
|
||||
NodeList evaluate(Document doc);
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package eu.dnetlib.validator2.validation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static eu.dnetlib.validator2.validation.guideline.Guideline.Result;
|
||||
|
||||
public class StandardValidationResult implements XMLApplicationProfile.ValidationResult {
|
||||
|
||||
private final String id;
|
||||
private final double score;
|
||||
private final Map<String, Result> results;
|
||||
|
||||
public StandardValidationResult(String id, double score, Map<String, Result> results) {
|
||||
this.id = id;
|
||||
this.score = score;
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double score() {
|
||||
return score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Result> results() {
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package eu.dnetlib.validator2.validation;
|
||||
|
||||
import eu.dnetlib.validator2.validation.task.ValidationTaskOutput;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface Validator<T> {
|
||||
|
||||
default void validate(T subject, Consumer<ValidationTaskOutput> outputConsumer) {
|
||||
validate(Executors.newSingleThreadExecutor(), subject, outputConsumer);
|
||||
}
|
||||
|
||||
void validate(ExecutorService executor, T subject, Consumer<ValidationTaskOutput> outputConsumer);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package eu.dnetlib.validator2.validation;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.Status;
|
||||
import eu.dnetlib.validator2.engine.builtins.XMLCardinalityRule;
|
||||
import eu.dnetlib.validator2.validation.guideline.ElementSpec;
|
||||
import eu.dnetlib.validator2.validation.guideline.Guideline;
|
||||
import eu.dnetlib.validator2.validation.guideline.SyntheticGuideline;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* An application-specific collection of guidelines
|
||||
*/
|
||||
public interface XMLApplicationProfile {
|
||||
|
||||
String name();
|
||||
|
||||
Collection<? extends Guideline<Document>> guidelines();
|
||||
|
||||
Guideline<Document> guideline(String guidelineName);
|
||||
|
||||
ValidationResult validate(String id, Document document);
|
||||
|
||||
int maxScore();
|
||||
|
||||
interface ValidationResult {
|
||||
|
||||
String id();
|
||||
|
||||
double score();
|
||||
|
||||
Map<String, Guideline.Result> results();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
public abstract class AbstractGuideline<T> implements Guideline<T> {
|
||||
|
||||
private final String name;
|
||||
private final int weight;
|
||||
|
||||
public AbstractGuideline(String name, int weight) {
|
||||
this.name = name;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
|
||||
// Provide only an alternate name for clarity.
|
||||
public interface AttributeSpec extends NodeSpec {
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,429 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.Predicates;
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class Builders {
|
||||
|
||||
@Deprecated
|
||||
public static ElementSpecBuilder forElement(String elementName) {
|
||||
return new ElementSpecBuilder(elementName);
|
||||
}
|
||||
|
||||
public static ElementSpecBuilder forOptionalElement(String elementName) {
|
||||
return new ElementSpecBuilder(elementName).optional();
|
||||
}
|
||||
|
||||
public static ElementSpecBuilder forOptionalRepeatableElement(String elementName) {
|
||||
return new ElementSpecBuilder(elementName).optionalRepeatable();
|
||||
}
|
||||
|
||||
public static ElementSpecBuilder forRecommendedElement(String elementName) {
|
||||
return new ElementSpecBuilder(elementName).recommended();
|
||||
}
|
||||
|
||||
public static ElementSpecBuilder forRecommendedRepeatableElement(String elementName) {
|
||||
return new ElementSpecBuilder(elementName).recommendedRepeatable();
|
||||
}
|
||||
|
||||
public static ElementSpecBuilder forMandatoryElement(String elementName, Cardinality cardinality) {
|
||||
return new ElementSpecBuilder(elementName).mandatory(cardinality);
|
||||
}
|
||||
|
||||
public static <T> ElementSpecBuilder forMandatoryIfApplicableElement(String elementName,
|
||||
Cardinality cardinality,
|
||||
Rule<Document> applicabilityRule) {
|
||||
return new ElementSpecBuilder(elementName).mandatoryIfApplicable(cardinality, applicabilityRule);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static ElementSpecBuilder forElement(String elementName,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality) {
|
||||
return Helper.buildElement(elementName, requirementLevel, cardinality);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static ElementSpecBuilder forElement(String elementName,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality,
|
||||
String... allowedValues) {
|
||||
return Helper.buildElement(elementName, requirementLevel, cardinality).allowedValues(allowedValues);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static ElementSpecBuilder forElement(String elementName,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality,
|
||||
Predicate<String> allowedValuesPredicate) {
|
||||
return Helper.buildElement(elementName, requirementLevel, cardinality).allowedValues(allowedValuesPredicate);
|
||||
}
|
||||
|
||||
static final Predicate<String> ALLOW_ALL_VALUES = (String value) -> true;
|
||||
|
||||
private static class DefaultNodeSpec implements NodeSpec {
|
||||
|
||||
private final String name;
|
||||
private final RequirementLevel requirementLevel;
|
||||
private final Cardinality cardinality;
|
||||
private final Predicate<String> allowedValuesPredicate;
|
||||
private final Rule<Document> applicabilityRule;
|
||||
|
||||
DefaultNodeSpec(String name,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
this.name = name;
|
||||
this.requirementLevel = requirementLevel;
|
||||
this.cardinality = cardinality;
|
||||
this.allowedValuesPredicate = allowedValuesPredicate;
|
||||
this.applicabilityRule = applicabilityRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nodeName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequirementLevel requirementLevel() {
|
||||
return requirementLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cardinality cardinality() {
|
||||
return cardinality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<String> allowedValuesPredicate() {
|
||||
return allowedValuesPredicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rule<Document> applicabilityRule() {
|
||||
return applicabilityRule;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultAttributeSpec extends DefaultNodeSpec implements AttributeSpec {
|
||||
|
||||
DefaultAttributeSpec(String name,
|
||||
RequirementLevel requirementLevel,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
super(name, requirementLevel, Cardinality.ONE, allowedValuesPredicate, applicabilityRule);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultElementSpec extends DefaultNodeSpec implements ElementSpec {
|
||||
|
||||
private final Set<String> parents;
|
||||
private final Set<AttributeSpec> attributeSpecs;
|
||||
private final Set<ElementSpec> subElementSpecs;
|
||||
private final String valuePrefix;
|
||||
private final ElementPosition elementPosition;
|
||||
|
||||
DefaultElementSpec(Set<String> parents,
|
||||
String name,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule,
|
||||
ElementPosition elementPosition,
|
||||
String valuePrefix,
|
||||
Set<AttributeSpec> attributeSpecs,
|
||||
Set<ElementSpec> subElementSpecs) {
|
||||
super(name, requirementLevel, cardinality, allowedValuesPredicate, applicabilityRule);
|
||||
this.parents = parents;
|
||||
this.elementPosition = elementPosition;
|
||||
this.valuePrefix = valuePrefix;
|
||||
this.attributeSpecs = attributeSpecs;
|
||||
this.subElementSpecs = subElementSpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> parents() {
|
||||
return parents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ElementSpec> subElementSpecs() {
|
||||
return subElementSpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AttributeSpec> attributeSpecs() {
|
||||
return attributeSpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valuePrefix() {
|
||||
return valuePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementPosition position() {
|
||||
return elementPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ElementSpecBuilder {
|
||||
|
||||
private final String elementName;
|
||||
private String[] parents;
|
||||
private RequirementLevel elementRequirementLevel;
|
||||
private Cardinality elementCardinality;
|
||||
private ElementPosition elementPosition;
|
||||
private String valuePrefix;
|
||||
private Predicate<String> allowedValuesPredicate = ALLOW_ALL_VALUES;
|
||||
private Set<AttributeSpec> attributeSpecs = new LinkedHashSet<>();
|
||||
private Set<ElementSpecBuilder> subElementSpecs = new LinkedHashSet<>();
|
||||
private Rule<Document> applicabilityRule;
|
||||
|
||||
private ElementSpecBuilder(String elementName) {
|
||||
this.elementName = elementName;
|
||||
}
|
||||
|
||||
private ElementSpecBuilder with(RequirementLevel requirementLevel, Cardinality cardinality) {
|
||||
elementRequirementLevel = requirementLevel;
|
||||
elementCardinality = cardinality;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElementSpecBuilder inContext(String... parentElementNames) {
|
||||
this.parents = parentElementNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
private ElementSpecBuilder optional() {
|
||||
return with(RequirementLevel.OPTIONAL, Cardinality.ONE);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder optionalRepeatable() {
|
||||
return with(RequirementLevel.OPTIONAL, Cardinality.ONE_TO_N);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder recommended() {
|
||||
return with(RequirementLevel.RECOMMENDED, Cardinality.ONE);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder recommendedRepeatable() {
|
||||
return with(RequirementLevel.RECOMMENDED, Cardinality.ONE_TO_N);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder mandatory(Cardinality cardinality) {
|
||||
return with(RequirementLevel.MANDATORY, cardinality);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder mandatoryIfApplicable(Cardinality cardinality, Rule<Document> applicabilityRule) {
|
||||
this.applicabilityRule = applicabilityRule;
|
||||
return with(RequirementLevel.MANDATORY_IF_APPLICABLE, cardinality);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder allowedValues(String... allowedValues) {
|
||||
allowedValuesPredicate = allowedValuesPredicateFor("Element", allowedValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElementSpecBuilder allowedValues(Predicate<String> allowedValuesPredicate) {
|
||||
this.allowedValuesPredicate = allowedValuesPredicate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElementSpecBuilder atPosition(ElementPosition elementOccurrence) {
|
||||
this.elementPosition = elementOccurrence;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElementSpecBuilder valueMustStartWith(String prefix) {
|
||||
String canonical = Helper.canonicalize(prefix);
|
||||
if (canonical.isEmpty()) {
|
||||
throw new IllegalArgumentException("Prefix cannot be empty");
|
||||
}
|
||||
this.valuePrefix = canonical;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withOptionalAttribute(String attributeName) {
|
||||
return withAttribute(attributeName, RequirementLevel.OPTIONAL, ALLOW_ALL_VALUES);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withOptionalAttribute(String attributeName, String... allowedValues) {
|
||||
return withAttribute(attributeName, RequirementLevel.OPTIONAL, allowedValues);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withOptionalAttribute(String attributeName, Predicate<String> allowedValuesPredicate) {
|
||||
return withAttribute(attributeName, RequirementLevel.OPTIONAL, allowedValuesPredicate);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withRecommendedAttribute(String attributeName) {
|
||||
return withAttribute(attributeName, RequirementLevel.RECOMMENDED, ALLOW_ALL_VALUES);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withRecommendedAttribute(String attributeName, String... allowedValues) {
|
||||
return withAttribute(attributeName, RequirementLevel.RECOMMENDED, allowedValues);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withRecommendedAttribute(String attributeName, Predicate<String> allowedValuesPredicate) {
|
||||
return withAttribute(attributeName, RequirementLevel.RECOMMENDED, allowedValuesPredicate);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withMandatoryAttribute(String attributeName) {
|
||||
return withAttribute(attributeName, RequirementLevel.MANDATORY, ALLOW_ALL_VALUES);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withMandatoryAttribute(String attributeName, String... allowedValues) {
|
||||
return withAttribute(attributeName, RequirementLevel.MANDATORY, allowedValues);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withMandatoryAttribute(String attributeName, Predicate<String> allowedValuesPredicate) {
|
||||
return withAttribute(attributeName, RequirementLevel.MANDATORY, allowedValuesPredicate);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withMandatoryIfApplicableAttribute(String attributeName,
|
||||
Rule<Document> applicabilityRule) {
|
||||
return withAttribute(attributeName, RequirementLevel.MANDATORY_IF_APPLICABLE, ALLOW_ALL_VALUES, applicabilityRule);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withMandatoryIfApplicableAttribute(String attributeName,
|
||||
Rule<Document> applicabilityRule,
|
||||
String... allowedValues) {
|
||||
return withAttribute(attributeName, RequirementLevel.MANDATORY_IF_APPLICABLE, allowedValuesPredicateFor("Attribute", allowedValues), applicabilityRule);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withMandatoryIfApplicableAttribute(String attributeName,
|
||||
Rule<Document> applicabilityRule,
|
||||
Predicate<String> allowedValuesPredicate) {
|
||||
return withAttribute(attributeName, RequirementLevel.MANDATORY_IF_APPLICABLE, allowedValuesPredicate, applicabilityRule);
|
||||
}
|
||||
|
||||
public ElementSpecBuilder withSubElement(ElementSpecBuilder subElementSpecBuilder) {
|
||||
subElementSpecs.add(subElementSpecBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElementSpec build() {
|
||||
|
||||
String canonical = Helper.ensureNonEmpty(elementName,
|
||||
() -> new IllegalStateException("Element name cannot be empty"));
|
||||
|
||||
validate("Element", elementName, elementRequirementLevel, elementCardinality, allowedValuesPredicate, applicabilityRule);
|
||||
|
||||
Set<String> setOfParents = parents == null ?
|
||||
Collections.emptySet() :
|
||||
Stream.of(parents).filter( p -> !Helper.isEmpty(p)).collect(Collectors.toSet());
|
||||
|
||||
if (elementPosition == null) {
|
||||
elementPosition = ElementPosition.ALL;
|
||||
}
|
||||
|
||||
return new DefaultElementSpec(
|
||||
setOfParents,
|
||||
canonical,
|
||||
elementRequirementLevel,
|
||||
elementCardinality,
|
||||
allowedValuesPredicate,
|
||||
applicabilityRule,
|
||||
elementPosition,
|
||||
valuePrefix,
|
||||
attributeSpecs,
|
||||
subElementSpecs.stream().map(ElementSpecBuilder::build).collect(Collectors.toCollection(LinkedHashSet::new))
|
||||
);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder withAttribute(String attrName,
|
||||
RequirementLevel requirementLevel) {
|
||||
return withAttribute(attrName, requirementLevel, ALLOW_ALL_VALUES, null);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder withAttribute(String attrName,
|
||||
RequirementLevel requirementLevel,
|
||||
String... allowedValues) {
|
||||
return withAttribute(attrName, requirementLevel, allowedValuesPredicateFor("Attribute", allowedValues), null);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder withAttribute(String attrName,
|
||||
RequirementLevel requirementLevel,
|
||||
Predicate<String> allowedValuesPredicate) {
|
||||
return withAttribute(attrName, requirementLevel, allowedValuesPredicate, null);
|
||||
}
|
||||
|
||||
private ElementSpecBuilder withAttribute(String attrName,
|
||||
RequirementLevel requirementLevel,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
String canonical = Helper.ensureNonEmpty(attrName,
|
||||
() -> new IllegalStateException("Attribute name cannot be empty"));
|
||||
validate("Attribute", attrName, requirementLevel, allowedValuesPredicate, applicabilityRule);
|
||||
attributeSpecs.add(new DefaultAttributeSpec(canonical, requirementLevel, allowedValuesPredicate, applicabilityRule));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static Predicate<String> allowedValuesPredicateFor(String type, String... allowedValues) {
|
||||
if (allowedValues == null || allowedValues.length == 0) {
|
||||
throw new IllegalArgumentException(type + " allowed values cannot be empty");
|
||||
}
|
||||
//TODO: Confirm that ignoring case is the right thing to do.
|
||||
Predicates.SetOfCaseInsensitiveAllowedValues setOfAllowedValues =
|
||||
new Predicates.SetOfCaseInsensitiveAllowedValues(allowedValues);
|
||||
if (setOfAllowedValues.isEmpty()) {
|
||||
throw new IllegalArgumentException(type + " allowed values cannot be empty");
|
||||
}
|
||||
return setOfAllowedValues;
|
||||
}
|
||||
|
||||
private static void validate(String type,
|
||||
String name,
|
||||
RequirementLevel requirementLevel,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
if (requirementLevel == null) {
|
||||
throw new IllegalStateException(type + ":" + name + " requirement level cannot be empty");
|
||||
}
|
||||
|
||||
if (allowedValuesPredicate == null) {
|
||||
throw new IllegalStateException(type + ":" + name + " allowed values predicate cannot be empty");
|
||||
}
|
||||
|
||||
validate(type, name, requirementLevel, applicabilityRule);
|
||||
}
|
||||
|
||||
private static void validate(String type,
|
||||
String name,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
validate(type, name, requirementLevel, allowedValuesPredicate, applicabilityRule);
|
||||
|
||||
if (cardinality == null) {
|
||||
throw new IllegalStateException(type + ":" + name + " cardinality cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate(String type, String name, RequirementLevel requirementLevel, Rule<Document> applicabilityRule) {
|
||||
if (requirementLevel == RequirementLevel.MANDATORY_IF_APPLICABLE &&
|
||||
applicabilityRule == null) {
|
||||
throw new IllegalStateException(type + ": " + name + " is invalid: required applicability rule is missing");
|
||||
}
|
||||
if (requirementLevel != RequirementLevel.MANDATORY_IF_APPLICABLE &&
|
||||
applicabilityRule != null) {
|
||||
throw new IllegalStateException(type + ": " + name + " is invalid: an applicability rule is present");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
public enum Cardinality {
|
||||
|
||||
ONE(1, 1),
|
||||
ONE_TO_N(1, Long.MAX_VALUE),
|
||||
TWO(2, 2),
|
||||
FOUR_TO_N(4, Long.MAX_VALUE);
|
||||
|
||||
private final long lowerBound;
|
||||
private final long upperBound;
|
||||
|
||||
private Cardinality(long lowerBound, long upperBound) {
|
||||
this.lowerBound = lowerBound;
|
||||
this.upperBound = upperBound;
|
||||
}
|
||||
|
||||
long getLowerBound() {
|
||||
return lowerBound;
|
||||
}
|
||||
|
||||
long getUpperBound() {
|
||||
return upperBound;
|
||||
}
|
||||
|
||||
public String asText() {
|
||||
return (lowerBound == upperBound)
|
||||
? String.valueOf(lowerBound)
|
||||
: lowerBound + "-" + ((upperBound == Long.MAX_VALUE) ? "n" : upperBound);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class CompilationResult {
|
||||
|
||||
SyntheticRule<Document> rootNodeRule;
|
||||
final List<SyntheticRule<Document>> nodeRules = new ArrayList<>();
|
||||
final Map<String, RequirementLevel> ruleIdToRequirementLevel = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "### CompilationResult ###\n" +
|
||||
"Root = " + Helper.stringify(rootNodeRule) + "\n" +
|
||||
"Node = [" + nodeRules.stream().map(Helper::stringify).collect(Collectors.joining(", ")) + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
public enum ElementPosition {
|
||||
|
||||
ALL(null),
|
||||
FIRST("position()=1"),
|
||||
SECOND("position()=2");
|
||||
|
||||
public final String xpath;
|
||||
|
||||
ElementPosition(String xpath) {
|
||||
this.xpath = xpath;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface ElementSpec extends NodeSpec {
|
||||
|
||||
String APPLICABILITY_RULE_ID = "ApplicabilityRule";
|
||||
|
||||
Set<String> parents();
|
||||
|
||||
Set<ElementSpec> subElementSpecs();
|
||||
|
||||
Set<AttributeSpec> attributeSpecs();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the required prefix or null if no prefix has been defined for the value
|
||||
*/
|
||||
String valuePrefix();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the specific position (occurrence) of the element the spec refers to
|
||||
*/
|
||||
ElementPosition position();
|
||||
|
||||
}
|
@ -0,0 +1,621 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import eu.dnetlib.validator2.engine.builtins.SimpleContext;
|
||||
import eu.dnetlib.validator2.engine.builtins.StandardXMLContext;
|
||||
import eu.dnetlib.validator2.engine.builtins.XMLRule;
|
||||
import eu.dnetlib.validator2.engine.builtins.XPathExpressionHelper;
|
||||
import eu.dnetlib.validator2.engine.contexts.XMLContext;
|
||||
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.XPathExpression;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class ElementSpecCompiler {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private static final String[] EMPTY = new String[]{};
|
||||
|
||||
private final List<ElementSpec> specs = new ArrayList<>();
|
||||
private final CompilationResult compilationResult = new CompilationResult();
|
||||
|
||||
CompilationResult compile(ElementSpec elementSpec, Supplier<GuidelineEvaluation> runtimeInfo) {
|
||||
ElementStruct rootElement = new ElementStruct(elementSpec, runtimeInfo);
|
||||
generateRulesForElement(rootElement);
|
||||
return compilationResult;
|
||||
}
|
||||
|
||||
private void generateRulesForElement(ElementStruct currentElement) {
|
||||
|
||||
specs.add(currentElement.spec);
|
||||
|
||||
if (currentElement.parent == null) {
|
||||
currentElement.createRootElemRule(compilationResult);
|
||||
}
|
||||
else {
|
||||
currentElement.createSubElemRule(compilationResult);
|
||||
}
|
||||
|
||||
for (AttributeSpec attrSpec : currentElement.spec.attributeSpecs()) {
|
||||
currentElement.createAttrRule(compilationResult, attrSpec);
|
||||
}
|
||||
|
||||
for(ElementSpec subElementSpec: currentElement.spec.subElementSpecs()) {
|
||||
generateRulesForElement(new ElementStruct(currentElement.elementsArray, subElementSpec, currentElement));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String[] copyAndAppend(String[] elements, String newElem) {
|
||||
int len = elements.length;
|
||||
String[] newElements = new String[len + 1];
|
||||
System.arraycopy(elements, 0, newElements, 0, len);
|
||||
newElements[len] = newElem;
|
||||
return newElements;
|
||||
}
|
||||
|
||||
// TODO: Should we escape the arg?
|
||||
private static String xpathForNodeName(String name) {
|
||||
return "local-name()='" + name + "'";
|
||||
}
|
||||
|
||||
private static String xpathForNodeName(String name, String valuePrefix) {
|
||||
String[] components = new String[] { xpathForNodeName(name) };
|
||||
if (valuePrefix != null) {
|
||||
components = copyAndAppend(components, "starts-with(normalize-space(text()), '" + valuePrefix + "')"); // normalize-space needed for cases text isn't in the same line with starting xml tag
|
||||
}
|
||||
return String.join(" and ", components);
|
||||
}
|
||||
|
||||
|
||||
// TODO: Should we escape the arg?
|
||||
private static String xpathForAttributeName(String name) {
|
||||
return "@" + name;
|
||||
}
|
||||
|
||||
private static String xpathWithText(String xpath, boolean withText) {
|
||||
return withText ? xpath + "/text()" : xpath;
|
||||
}
|
||||
|
||||
static String nodeRuleIdFor(String nodeType,
|
||||
String nodeName,
|
||||
RequirementLevel requirementLevel,
|
||||
ElementPosition position,
|
||||
String valuePrefix,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Cardinality cardinality) {
|
||||
|
||||
StringBuilder builder =
|
||||
new StringBuilder(requirementLevel.name()).append(" rule: The ").append(nodeName).append(" ").append(nodeType);
|
||||
|
||||
if (position != ElementPosition.ALL) {
|
||||
builder.append(" at ").append(position.name().toLowerCase()).append(" position");
|
||||
}
|
||||
|
||||
if (valuePrefix != null) {
|
||||
builder.append(" has values that start with ").append(valuePrefix).append(",");
|
||||
}
|
||||
|
||||
if (!allowsAllValues(allowedValuesPredicate)) {
|
||||
builder.append(" contains allowed values only,");
|
||||
}
|
||||
|
||||
builder.append(" has cardinality ").append(cardinality.asText());
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
static String cardinalityRuleIdFor(String nodeName,
|
||||
RequirementLevel requirementLevel,
|
||||
Cardinality cardinality,
|
||||
String valuePrefix) {
|
||||
String id = requirementLevel.name() + " rule: " + nodeName + " has cardinality " + cardinality.asText();
|
||||
if(Helper.isEmpty(valuePrefix)) {
|
||||
return id;
|
||||
}
|
||||
else {
|
||||
return id + " for values starting with '" + valuePrefix + "'";
|
||||
}
|
||||
}
|
||||
|
||||
static final String mergeNodeNames(String... names) {
|
||||
return Stream.of(names).collect(Collectors.joining("/"));
|
||||
}
|
||||
|
||||
|
||||
// Heuristic for avoiding to create rules for checking the allowed values, when all values are allowed.
|
||||
private static boolean allowsAllValues(Predicate<String> allowedValuesPredicate) {
|
||||
return (allowedValuesPredicate == null || // null value will never occur through our Builders class.
|
||||
allowedValuesPredicate == Builders.ALLOW_ALL_VALUES
|
||||
// || allowedValuesPredicate.test(null) // this seems to give undesired results against allowedValues negated predicates
|
||||
);
|
||||
}
|
||||
|
||||
private static SyntheticRule<Document> createActualRootElementRule(Supplier<GuidelineEvaluation> runtimeInfo,
|
||||
String id,
|
||||
String xpath,
|
||||
String xpathForValue,
|
||||
Cardinality cardinality,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
XMLContext ctx = createCustomXMLContext(id, xpath);
|
||||
Predicate<Integer> lengthPredicate = Helper.
|
||||
createCardinalityPredicate(cardinality.getLowerBound(), cardinality.getUpperBound(), true);
|
||||
Predicate<NodeList> lengthChecker = (NodeList elements) -> lengthPredicate.test(elements.getLength());
|
||||
|
||||
if (allowsAllValues(allowedValuesPredicate)) {
|
||||
return new RootElemRule(ctx, lengthChecker, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
else {
|
||||
Predicate<NodeList> lengthCheckerOnAllowedElements = (NodeList elements) -> {
|
||||
NodeList filtered = filterNodes(elements, xpathForValue, allowedValuesPredicate);
|
||||
return lengthChecker.test(filtered);
|
||||
};
|
||||
return new RootElemRule(ctx, lengthCheckerOnAllowedElements, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// The NodeList contains the parent elements
|
||||
private static SyntheticRule<Document> createActualAttributeSpecRule(Supplier<GuidelineEvaluation> runtimeInfo,
|
||||
SyntheticRule<Document> parentRule,
|
||||
String id,
|
||||
String attrName,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
SimpleContext ctx = new SimpleContext(id);
|
||||
|
||||
if (allowsAllValues(allowedValuesPredicate)) {
|
||||
return new NodeSpecRule<>(ctx, parentRule, (NodeList elements) -> {
|
||||
NodeList attrs = attributesOf(elements, attrName, (String s) -> !Helper.isEmpty(s));
|
||||
return (elements != null && attrs.getLength() == elements.getLength());
|
||||
}, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
else {
|
||||
Predicate<NodeList> allowedAttrsLengthEqualsElemLength = (NodeList elements) -> {
|
||||
NodeList attrs = attributesOf(elements, attrName, allowedValuesPredicate);
|
||||
return (elements != null && elements.getLength() == attrs.getLength());
|
||||
};
|
||||
return new NodeSpecRule<>(ctx, parentRule, allowedAttrsLengthEqualsElemLength, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private static SyntheticRule<Document> createActualSubElementRule(Supplier<GuidelineEvaluation> runtimeInfo,
|
||||
SyntheticRule<Document> parentRule,
|
||||
String id,
|
||||
String xpath,
|
||||
String xpathForValue,
|
||||
Cardinality cardinality,
|
||||
Predicate<String> allowedValuesPredicate,
|
||||
Rule<Document> applicabilityRule) {
|
||||
XMLContext ctx = createCustomXMLContext(id, xpath);
|
||||
|
||||
Predicate<NodeList> lengthChecker = (NodeList elements) -> {
|
||||
// Calculate the length predicate at rule runtime
|
||||
Predicate<Integer> lengthPredicate = createLengthPredicateForSubElements(runtimeInfo, parentRule, cardinality);
|
||||
return lengthPredicate.test(elements.getLength());
|
||||
};
|
||||
|
||||
if (allowsAllValues(allowedValuesPredicate)) {
|
||||
return new SubElemRule(ctx, parentRule, lengthChecker, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
else {
|
||||
Predicate<NodeList> lengthCheckerOnAllowedElements = (NodeList elements) -> {
|
||||
NodeList filtered = filterNodes(elements, xpathForValue, allowedValuesPredicate);
|
||||
return lengthChecker.test(filtered);
|
||||
};
|
||||
return new SubElemRule(ctx, parentRule, lengthCheckerOnAllowedElements, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private static Predicate<Integer> createLengthPredicateForSubElements(Supplier<GuidelineEvaluation> runtimeInfo,
|
||||
SyntheticRule<Document> parentRule,
|
||||
Cardinality cardinality) {
|
||||
GuidelineEvaluation evaluation = runtimeInfo.get();
|
||||
NodeList parents = evaluation.getNodesOf(parentRule.getContext().getIdProperty().getValue());
|
||||
if (parents == null) {
|
||||
return (Integer count) -> false; // There are no parents, actually
|
||||
}
|
||||
int parentsLength = parents.getLength();
|
||||
if (cardinality == Cardinality.ONE) {
|
||||
return (Integer count) -> count == parentsLength; // A sub-element can be present in each parent
|
||||
}
|
||||
else if (cardinality == Cardinality.ONE_TO_N) {
|
||||
return (Integer count) -> count >= parentsLength; // One or more sub-elements can be present in each parent
|
||||
}
|
||||
else if (cardinality == Cardinality.TWO) {
|
||||
return (Integer count) -> count == parentsLength * 2; // Two sub-elements can be present in each parent
|
||||
}
|
||||
else if (cardinality == Cardinality.FOUR_TO_N){
|
||||
return (Integer count) -> count >= parentsLength * 4; // Four or more sub-elements can be present in each parent
|
||||
}
|
||||
else{ // not reachable
|
||||
throw new RuleEvaluationException(" Unsupported cardinality " + cardinality, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static NodeList attributesOf(NodeList elements, String attrName, Predicate<String> allowedValues) {
|
||||
return Helper.nodesThatMatchThePredicate(
|
||||
elements,
|
||||
(Node node) -> Helper.getAttributeValue(node, attrName),
|
||||
allowedValues
|
||||
);
|
||||
}
|
||||
|
||||
private static XMLContext createCustomXMLContext(String id, String xpath) {
|
||||
StandardXMLContext context = new StandardXMLContext();
|
||||
context.getIdProperty().setValue(id);
|
||||
context.getXPathExpressionProperty().setValue(xpath);
|
||||
context.getNodeListActionProperty().setValue("custom");
|
||||
return context;
|
||||
}
|
||||
|
||||
private static NodeList filterNodes(NodeList nodes, String xpathToReadValues, Predicate<String> allowedValuesPredicate) {
|
||||
logger.debug("Filtering nodes with {}", xpathToReadValues);
|
||||
return Helper.nodesThatMatchThePredicate(nodes, new NodeValueReader(xpathToReadValues), allowedValuesPredicate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class NodeValueReader implements Function<Node, String> {
|
||||
|
||||
private final String nodeExpr;
|
||||
|
||||
NodeValueReader(String nodeExpr) {
|
||||
this.nodeExpr = nodeExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(Node parent) {
|
||||
logger.debug("Reading node value {} from parent {}", nodeExpr, parent);
|
||||
Node node = XPathExpressionHelper.node(nodeExpr, parent);
|
||||
return node == null ? null : node.getNodeValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ElementStruct {
|
||||
|
||||
Supplier<GuidelineEvaluation> runtimeInfo;
|
||||
final ElementSpec spec;
|
||||
final String[] parentElementNames;
|
||||
final ElementStruct parent;
|
||||
//e.g. [elem1,sub1]
|
||||
final String[] elementsArray;
|
||||
// e.g. elem1/sub1
|
||||
final String elementsId;
|
||||
SyntheticRule<Document> rule;
|
||||
|
||||
ElementStruct(String[] parentElementNames, ElementSpec spec, ElementStruct parent) {
|
||||
this.runtimeInfo = parent.runtimeInfo;
|
||||
this.parentElementNames = parentElementNames;
|
||||
this.spec = spec;
|
||||
this.parent = parent;
|
||||
elementsArray = copyAndAppend(parentElementNames, spec.nodeName());
|
||||
// e.g. elem1/sub1
|
||||
elementsId = mergeNodeNames(elementsArray);
|
||||
}
|
||||
|
||||
ElementStruct(ElementSpec spec, Supplier<GuidelineEvaluation> runtimeInfo) {
|
||||
this.runtimeInfo = runtimeInfo;
|
||||
this.parentElementNames = EMPTY;
|
||||
this.spec = spec;
|
||||
this.parent = null;
|
||||
elementsArray = copyAndAppend(parentElementNames, spec.nodeName());
|
||||
// e.g. elem1/sub1
|
||||
elementsId = mergeNodeNames(elementsArray);
|
||||
}
|
||||
|
||||
private String xpath(boolean withText) {
|
||||
String xpathForThis = xpathForNodeName(spec.nodeName(), spec.valuePrefix());
|
||||
String xpath;
|
||||
if (parent == null) {
|
||||
// This is the top-level element rule
|
||||
List<String> pathComponents = spec.parents().stream().
|
||||
map( s -> "*[" + xpathForNodeName(s) + "]").collect(Collectors.toList());
|
||||
pathComponents.add("*[" + xpathForThis + "]");
|
||||
xpath = "//" + String.join("/", pathComponents);
|
||||
}
|
||||
else {
|
||||
xpath = parent.xpath(false) + "/*[" + xpathForThis + "]";
|
||||
}
|
||||
if (spec.position().xpath != null) {
|
||||
xpath = "(" + xpath + ")[" + spec.position().xpath + "]";
|
||||
}
|
||||
// System.out.println(xpath);
|
||||
return xpathWithText(xpath, withText);
|
||||
}
|
||||
|
||||
private void createRootElemRule(CompilationResult compilationResult) {
|
||||
String id = nodeRuleIdFor(
|
||||
"Element",
|
||||
elementsId,
|
||||
spec.requirementLevel(),
|
||||
spec.position(),
|
||||
spec.valuePrefix(),
|
||||
spec.allowedValuesPredicate(),
|
||||
spec.cardinality()
|
||||
);
|
||||
|
||||
SyntheticRule<Document> rule = createActualRootElementRule(
|
||||
runtimeInfo,
|
||||
id,
|
||||
xpath(false),
|
||||
"text()",
|
||||
spec.cardinality(),
|
||||
spec.allowedValuesPredicate(),
|
||||
spec.applicabilityRule()
|
||||
);
|
||||
|
||||
compilationResult.ruleIdToRequirementLevel.put(id, spec.requirementLevel());
|
||||
compilationResult.rootNodeRule = rule;
|
||||
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
private void createAttrRule(CompilationResult compilationResult, AttributeSpec attrSpec) {
|
||||
String attrRuleIdComponent = mergeNodeNames(elementsId, attrSpec.nodeName());
|
||||
String attrRuleId = nodeRuleIdFor(
|
||||
"Attribute",
|
||||
attrRuleIdComponent,
|
||||
attrSpec.requirementLevel(),
|
||||
ElementPosition.ALL,
|
||||
null,
|
||||
spec.allowedValuesPredicate(),
|
||||
spec.cardinality()
|
||||
);
|
||||
|
||||
SyntheticRule<Document> rule = createActualAttributeSpecRule(
|
||||
runtimeInfo,
|
||||
this.rule,
|
||||
attrRuleId,
|
||||
attrSpec.nodeName(), // this is resolved in the context of its parent elements
|
||||
attrSpec.allowedValuesPredicate(),
|
||||
attrSpec.applicabilityRule()
|
||||
);
|
||||
|
||||
compilationResult.ruleIdToRequirementLevel.put(attrRuleId, attrSpec.requirementLevel());
|
||||
compilationResult.nodeRules.add(rule);
|
||||
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
private void createSubElemRule(CompilationResult compilationResult) {
|
||||
String id = nodeRuleIdFor(
|
||||
"Element",
|
||||
elementsId,
|
||||
spec.requirementLevel(),
|
||||
spec.position(),
|
||||
spec.valuePrefix(),
|
||||
spec.allowedValuesPredicate(),
|
||||
spec.cardinality()
|
||||
);
|
||||
|
||||
SyntheticRule<Document> rule = createActualSubElementRule(
|
||||
runtimeInfo,
|
||||
parent.rule,
|
||||
id,
|
||||
xpath(false),
|
||||
"text()",
|
||||
spec.cardinality(),
|
||||
spec.allowedValuesPredicate(),
|
||||
spec.applicabilityRule()
|
||||
);
|
||||
|
||||
compilationResult.ruleIdToRequirementLevel.put(id, spec.requirementLevel());
|
||||
compilationResult.nodeRules.add(rule);
|
||||
|
||||
this.rule = rule;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RootElemRule extends ElemRule {
|
||||
|
||||
RootElemRule(XMLContext context,
|
||||
Predicate<NodeList> nodeListPredicate,
|
||||
Rule<Document> applicabilityRule,
|
||||
Supplier<GuidelineEvaluation> runtimeInfo) {
|
||||
super(context, null, nodeListPredicate, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Document doc) throws RuleEvaluationException {
|
||||
GuidelineEvaluation guidelineEvaluation = runtimeInfo.get();
|
||||
String thisId = getContext().getIdProperty().getValue();
|
||||
|
||||
if (isApplicable(this, guidelineEvaluation).test(doc)) {
|
||||
try {
|
||||
logger.debug("Applying {}", thisId);
|
||||
NodeList nodes = getContext().getXPathExpressionProperty().evaluate(doc);
|
||||
boolean result = predicate.test(nodes);
|
||||
if (result) {
|
||||
logger.debug("Setting node list of this rule {}", thisId);
|
||||
guidelineEvaluation.setNodesOf(thisId, nodes);
|
||||
}
|
||||
return result;
|
||||
} catch (Throwable t) {
|
||||
throw new RuleEvaluationException(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SubElemRule extends ElemRule {
|
||||
|
||||
SubElemRule(XMLContext context,
|
||||
SyntheticRule<Document> parentRule,
|
||||
Predicate<NodeList> nodeListPredicate,
|
||||
Rule<Document> applicabilityRule,
|
||||
Supplier<GuidelineEvaluation> runtimeInfo) {
|
||||
super(context, parentRule, nodeListPredicate, applicabilityRule, runtimeInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Document doc) throws RuleEvaluationException {
|
||||
GuidelineEvaluation guidelineEvaluation = runtimeInfo.get();
|
||||
String thisId = getContext().getIdProperty().getValue();
|
||||
String parentRuleId = parentRule.getContext().getIdProperty().getValue();
|
||||
|
||||
if (guidelineEvaluation.getRequirementLevelOf(parentRuleId) == RequirementLevel.NOT_APPLICABLE) {
|
||||
// Our parent is not applicable, set ourselves as not_applicable too and silently pass
|
||||
guidelineEvaluation.setRequirementLevelOf(thisId, RequirementLevel.NOT_APPLICABLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isApplicable(this, guidelineEvaluation).test(doc)) {
|
||||
try {
|
||||
logger.debug("Appying {}", thisId);;
|
||||
NodeList nodes = getContext().getXPathExpressionProperty().evaluate(doc);
|
||||
boolean result = predicate.test(nodes);
|
||||
if (result) {
|
||||
logger.debug("Setting node list of this rule {}", thisId);
|
||||
guidelineEvaluation.setNodesOf(thisId, nodes);
|
||||
}
|
||||
return result;
|
||||
} catch (Throwable t) {
|
||||
throw new RuleEvaluationException(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ElemRule extends XMLRule<XMLContext> implements SyntheticRule<Document> {
|
||||
|
||||
protected final SyntheticRule<Document> parentRule;
|
||||
protected final Supplier<GuidelineEvaluation> runtimeInfo;
|
||||
protected final Rule<Document> applicabilityRule;
|
||||
|
||||
ElemRule(XMLContext context,
|
||||
SyntheticRule<Document> parentRule,
|
||||
Predicate<NodeList> nodeListPredicate,
|
||||
Rule<Document> applicabilityRule,
|
||||
Supplier<GuidelineEvaluation> runtimeInfo) {
|
||||
super(context, nodeListPredicate);
|
||||
this.parentRule = parentRule;
|
||||
this.applicabilityRule = applicabilityRule;
|
||||
this.runtimeInfo = runtimeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyntheticRule<Document> parentRule() {
|
||||
return parentRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rule<Document> applicabilityRule() {
|
||||
return applicabilityRule;
|
||||
}
|
||||
}
|
||||
|
||||
private static class NodeSpecRule<C extends RuleContext> implements SyntheticRule<Document> {
|
||||
|
||||
private final C context;
|
||||
private final Predicate<NodeList> nodeListPredicate;
|
||||
private final Rule<Document> applicabilityRule;
|
||||
private final SyntheticRule<Document> parentRule;
|
||||
private final Supplier<GuidelineEvaluation> runtimeInfo;
|
||||
|
||||
NodeSpecRule(C context,
|
||||
SyntheticRule<Document> parentRule,
|
||||
Predicate<NodeList> nodeListPredicate,
|
||||
Rule<Document> applicabilityRule,
|
||||
Supplier<GuidelineEvaluation> runtimeInfo) {
|
||||
this.context = context;
|
||||
this.parentRule = parentRule;
|
||||
this.nodeListPredicate = nodeListPredicate;
|
||||
this.applicabilityRule = applicabilityRule;
|
||||
this.runtimeInfo = runtimeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyntheticRule<Document> parentRule() {
|
||||
return parentRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rule<Document> applicabilityRule() {
|
||||
return applicabilityRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Document doc) throws RuleEvaluationException {
|
||||
|
||||
GuidelineEvaluation guidelineEvaluation = runtimeInfo.get();
|
||||
String thisId = getContext().getIdProperty().getValue();
|
||||
String parentRuleId = parentRule.getContext().getIdProperty().getValue();
|
||||
|
||||
if (guidelineEvaluation.getRequirementLevelOf(parentRuleId) == RequirementLevel.NOT_APPLICABLE) {
|
||||
// Our parent is not applicable, set ourselves as not_applicable too and silently pass
|
||||
guidelineEvaluation.setRequirementLevelOf(thisId, RequirementLevel.NOT_APPLICABLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isApplicable(this, guidelineEvaluation).test(doc)) {
|
||||
// We just use the doc to lookup the nodes of our parent
|
||||
NodeList nodes = guidelineEvaluation.getNodesOf(parentRuleId);
|
||||
logger.debug("Acquired node list of parent rule {} = {}", parentRuleId, (nodes == null ? "null" : nodes.getLength()));
|
||||
boolean result = nodeListPredicate.test(nodes);
|
||||
if (result) {
|
||||
logger.debug("Setting node list of this rule {}", thisId);
|
||||
guidelineEvaluation.setNodesOf(thisId, nodes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getContext().getIdProperty().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static Predicate<Document> isApplicable(SyntheticRule<Document> rule,
|
||||
GuidelineEvaluation guidelineEvaluation) {
|
||||
|
||||
return (Document doc) -> {
|
||||
Rule<Document> applicabilityRule = rule.applicabilityRule();
|
||||
if (applicabilityRule == null) {
|
||||
logger.debug("Null applicability rule of {}", rule);
|
||||
return true;
|
||||
}
|
||||
|
||||
String thisId = rule.getContext().getIdProperty().getValue();
|
||||
if (applicabilityRule.test(doc)) {
|
||||
logger.debug("Success of applicability rule of {}", rule);
|
||||
guidelineEvaluation.setRequirementLevelOf(thisId, RequirementLevel.MANDATORY);
|
||||
return true;
|
||||
} else {
|
||||
logger.debug("Failure of applicability rule of {}", rule);
|
||||
guidelineEvaluation.setRequirementLevelOf(thisId, RequirementLevel.NOT_APPLICABLE);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Status;
|
||||
|
||||
public interface Guideline<T> {
|
||||
|
||||
int getWeight(); //that's the "score" of the guideline if it succeeds
|
||||
|
||||
String getName();
|
||||
|
||||
default Result validate(T t) {
|
||||
return validate(t == null ? "Object" : t.getClass().getSimpleName(), t);
|
||||
}
|
||||
|
||||
Result validate(String id, T t);
|
||||
|
||||
interface Result {
|
||||
|
||||
int score();
|
||||
|
||||
Status status();
|
||||
|
||||
// When status == SUCCESS, potential warnings are held here
|
||||
// This may also contain messages when status == FAILURE
|
||||
Iterable<String> warnings();
|
||||
|
||||
// When status == FAILURE, the errors are held here
|
||||
// We currently hold a single error (and fail fast)
|
||||
Iterable<String> errors();
|
||||
|
||||
// When status == ERROR, the internal error is held here
|
||||
String internalError();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.*;
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.builtins.StandardRuleDiagnostics;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
class GuidelineEvaluation {
|
||||
|
||||
// TODO: Report all rule diagnostics to System out/err (we should remove this or allow clients to disable it)
|
||||
private static final RuleDiagnostics<Document, Rule<Document>> OUT = Helper.Diagnostics.systemOut();
|
||||
private static final RuleDiagnostics<Document, Rule<Document>> ERR = Helper.Diagnostics.systemErr();
|
||||
|
||||
private final String subjectId;
|
||||
private final Document doc;
|
||||
private final int weight;
|
||||
private final List<String> warnings = new ArrayList<>();
|
||||
private final List<String> errors = new ArrayList<>();
|
||||
private Map<String, RequirementLevel> ruleIdToRequirementLevel = new HashMap<>();
|
||||
private Map<String, NodeList> ruleIdToNodeList = new HashMap<>();
|
||||
private final Diagnostics diagnostics = new Diagnostics();
|
||||
private final Reporter<Document, SyntheticRule<Document>> reporter = new Reporter<>(diagnostics);
|
||||
|
||||
GuidelineEvaluation(String subjectId, Document doc, int weight) {
|
||||
this.subjectId = subjectId;
|
||||
this.doc = doc;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
Guideline.Result evaluate(CompilationResult result) {
|
||||
|
||||
ruleIdToRequirementLevel.putAll(result.ruleIdToRequirementLevel);
|
||||
|
||||
List<SyntheticRule<Document>> rules = new ArrayList<>();
|
||||
rules.add(result.rootNodeRule);
|
||||
rules.addAll(result.nodeRules);
|
||||
|
||||
// System.out.println("Evaluating " + rules);
|
||||
|
||||
for (SyntheticRule<Document> rule: rules) {
|
||||
|
||||
String id = rule.getContext().getIdProperty().getValue();
|
||||
|
||||
RuleEngine.applyAndReport(rule, doc, reporter);
|
||||
|
||||
Status status = diagnostics.getLastReportedStatus();
|
||||
if (status == Status.ERROR) {
|
||||
// fail fast in case of errors (no reason to proceed, since results may be rubbish)
|
||||
return StandardResult.forError(diagnostics.getLastReportedError().getMessage());
|
||||
}
|
||||
|
||||
if (status == Status.SUCCESS && getRequirementLevelOf(id) == RequirementLevel.NOT_APPLICABLE) {
|
||||
// Report the non-applicability of a rule as a warning
|
||||
// The check for both status and non-applicable requirement level is redundant
|
||||
// (non-applicable rules always succeed), yet it is more clear hopefully.
|
||||
// System.out.println("Warn for non-applicable: " + rule);
|
||||
warnings.add(synthesizeFailureMessage(rule));
|
||||
}
|
||||
else if (status == Status.FAILURE) {
|
||||
if (getRequirementLevelOf(id) == RequirementLevel.MANDATORY) {
|
||||
// A mandatory rule has failed, yet we don't know whether we should report is as such.
|
||||
|
||||
// Let's check the parent of the rule
|
||||
if (rule.parentRule() == null) {
|
||||
// This is the root rule failing!
|
||||
// Fail fast here, too (don't waste resources to evaluate other rules).
|
||||
// We will "enable" it, if it is requested.
|
||||
// System.out.println("Fail fast for root failure: " + rule);
|
||||
errors.add(synthesizeFailureMessage(rule));
|
||||
return StandardResult.forFailure(warnings, errors);
|
||||
}
|
||||
else {
|
||||
// The current rule has a parent/ancestor which:
|
||||
// (a) is non-mandatory or
|
||||
// (b) it was successful.
|
||||
// Thus, here we need only to warn and not to err, allowing the evaluation loop to proceed.
|
||||
// System.out.println("Warn for mandatory failure: " + rule);
|
||||
warnings.add(synthesizeFailureMessage(rule));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is a warning: a non-mandatory rule has failed.
|
||||
// Note that MA rules are treated as non-mandatory.
|
||||
// We let the evaluation loop proceed.
|
||||
// System.out.println("Warn for optional/recommended failure: " + rule);
|
||||
warnings.add(synthesizeFailureMessage(rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int returnedWeight = weight;
|
||||
if (rules.size() == warnings.size()) {
|
||||
// TODO: I think this is the desired behavior, but we need to get confirmation for this.
|
||||
// All rules have failed, but with warnings (thus being either optional or recommended).
|
||||
// This indicates a success with 0 weight.
|
||||
returnedWeight = 0;
|
||||
}
|
||||
return StandardResult.forSuccess(returnedWeight, warnings);
|
||||
}
|
||||
|
||||
private String synthesizeFailureMessage(Rule<Document> rule) {
|
||||
|
||||
return subjectId + ": rule " + Helper.stringify(rule) + " has failed";
|
||||
}
|
||||
|
||||
private String synthesizeNotApplicableMessage(Rule<Document> rule) {
|
||||
|
||||
return subjectId + ": rule " + Helper.stringify(rule) + " is not applicable";
|
||||
}
|
||||
|
||||
|
||||
void setNodesOf(String ruleId, NodeList nodes) {
|
||||
ruleIdToNodeList.put(ruleId, nodes);
|
||||
}
|
||||
|
||||
NodeList getNodesOf(String ruleId) {
|
||||
return ruleIdToNodeList.get(ruleId);
|
||||
}
|
||||
|
||||
void setRequirementLevelOf(String ruleId, RequirementLevel requirementLevel) {
|
||||
ruleIdToRequirementLevel.put(ruleId, requirementLevel);
|
||||
}
|
||||
|
||||
RequirementLevel getRequirementLevelOf(String ruleId) {
|
||||
return ruleIdToRequirementLevel.get(ruleId);
|
||||
}
|
||||
|
||||
|
||||
private static final class Diagnostics extends StandardRuleDiagnostics<Document, SyntheticRule<Document>> {
|
||||
|
||||
private final Map<String, Status> statusByRuleId = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void success(SyntheticRule<Document> rule, Document document) {
|
||||
OUT.success(rule, document);
|
||||
super.success(rule, document);
|
||||
statusByRuleId.put(rule.getContext().getIdProperty().getValue(), Status.SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(SyntheticRule<Document> rule, Document document) {
|
||||
OUT.failure(rule, document);
|
||||
super.failure(rule, document);
|
||||
statusByRuleId.put(rule.getContext().getIdProperty().getValue(), Status.FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(SyntheticRule<Document> rule, Document document, Throwable err) {
|
||||
ERR.error(rule, document, err);
|
||||
super.error(rule, document, err);
|
||||
statusByRuleId.put(rule.getContext().getIdProperty().getValue(), Status.ERROR);
|
||||
}
|
||||
|
||||
private Status statusFor(String ruleId) {
|
||||
return statusByRuleId.get(ruleId);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface NodeSpec {
|
||||
|
||||
String nodeName();
|
||||
|
||||
RequirementLevel requirementLevel();
|
||||
|
||||
Cardinality cardinality();
|
||||
|
||||
Predicate<String> allowedValuesPredicate();
|
||||
|
||||
// This can be null
|
||||
Rule<Document> applicabilityRule();
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
public enum RequirementLevel {
|
||||
|
||||
MANDATORY,
|
||||
MANDATORY_IF_APPLICABLE,
|
||||
RECOMMENDED,
|
||||
OPTIONAL,
|
||||
NOT_APPLICABLE;
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleDiagnostics;
|
||||
|
||||
public interface RuleEvaluator<T, R extends Rule<T>> {
|
||||
|
||||
RuleDiagnostics<T, R> evaluate(T subject);
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Status;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class StandardResult implements Guideline.Result {
|
||||
|
||||
private static final List<String> EMPTY = Collections.emptyList();
|
||||
|
||||
private final List<String> warnings;
|
||||
private final List<String> errors;
|
||||
private final String internalError;
|
||||
private final Status status;
|
||||
private final int score;
|
||||
|
||||
private StandardResult(int score, Status status, List<String> warnings, List<String> errors, String internalError) {
|
||||
this.status = status;
|
||||
this.score = score;
|
||||
this.warnings = warnings;
|
||||
this.errors = errors;
|
||||
this.internalError = internalError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int score() {
|
||||
return score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> warnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> errors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String internalError() {
|
||||
return internalError;
|
||||
}
|
||||
|
||||
public static StandardResult forError(String message) {
|
||||
return new StandardResult(-1, Status.ERROR, EMPTY, EMPTY, message);
|
||||
}
|
||||
|
||||
public static StandardResult forSuccess(int score, List<String> warnings) {
|
||||
return new StandardResult(score, Status.SUCCESS, sanitize(warnings), EMPTY, null);
|
||||
}
|
||||
|
||||
public static StandardResult forFailure(List<String> warnings, List<String> errors) {
|
||||
return new StandardResult(0, Status.FAILURE, sanitize(warnings), sanitize(errors), null);
|
||||
}
|
||||
|
||||
private static List<String> sanitize(List<String> list) {
|
||||
if (list == null || list.size() == 0) return EMPTY;
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (status == Status.SUCCESS || status == Status.FAILURE) {
|
||||
return status + " (" + warnings.size() + " warnings) - score " + score;
|
||||
}
|
||||
else {
|
||||
return status + " (" + warnings.size() + " warnings) - " + internalError;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Helper;
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import eu.dnetlib.validator2.engine.RuleContext;
|
||||
import eu.dnetlib.validator2.engine.RuleEvaluationException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
public final class SyntheticGuideline extends AbstractGuideline<Document> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private CompilationResult compilationResult;
|
||||
private static final ThreadLocal<GuidelineEvaluation> evaluation = new ThreadLocal<>();
|
||||
|
||||
private SyntheticGuideline(String name, int weight) {
|
||||
super(name, weight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result validate(String id, Document document) {
|
||||
logger.debug("Validating document {} in {}", id, Thread.currentThread());
|
||||
evaluation.set(new GuidelineEvaluation(id, document, getWeight()));
|
||||
Result result = evaluation.get().evaluate(compilationResult);
|
||||
logger.debug("Evaluated: {} in {} with result {}", evaluation.get(), Thread.currentThread(), result);
|
||||
evaluation.remove();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SyntheticGuideline of(String name, int weight, ElementSpec elementSpec) {
|
||||
String canonical = Helper.ensureNonEmpty(name, () -> new IllegalArgumentException("Name cannot be empty"));
|
||||
|
||||
// Here we define a guideline with a specific elementSpec as origin, we cannot allow null
|
||||
if (elementSpec == null) {
|
||||
throw new IllegalArgumentException("ElementSpec cannot be empty");
|
||||
}
|
||||
|
||||
if (weight < 0) {
|
||||
throw new IllegalArgumentException("Weight cannot be negative");
|
||||
}
|
||||
|
||||
SyntheticGuideline guideline = new SyntheticGuideline(canonical, weight);
|
||||
guideline.compilationResult = new ElementSpecCompiler().compile(elementSpec, evaluation::get);
|
||||
return guideline;
|
||||
}
|
||||
|
||||
// Note: the synthetic rules that will be created are completely independent of each other
|
||||
public static SyntheticGuideline of(String name, int weight, RequirementLevel requirementLevel, Rule<Document> rule) {
|
||||
String canonical = Helper.ensureNonEmpty(name, () -> new IllegalArgumentException("Name cannot be empty"));
|
||||
|
||||
if (weight < 0) {
|
||||
throw new IllegalArgumentException("Weight cannot be negative");
|
||||
}
|
||||
|
||||
if (requirementLevel == null) {
|
||||
throw new IllegalArgumentException("Requirement level cannot be empty");
|
||||
}
|
||||
|
||||
if (rule == null) {
|
||||
throw new IllegalArgumentException("Rule cannot be empty");
|
||||
}
|
||||
|
||||
SyntheticGuideline guideline = new SyntheticGuideline(canonical, weight);
|
||||
CompilationResult compilationResult = new CompilationResult();
|
||||
compilationResult.rootNodeRule = new SyntheticRule<Document>() {
|
||||
@Override
|
||||
public SyntheticRule<Document> parentRule() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rule<Document> applicabilityRule() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <C extends RuleContext> C getContext() {
|
||||
return rule.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Document document) throws RuleEvaluationException {
|
||||
return rule.test(document);
|
||||
}
|
||||
};
|
||||
compilationResult.ruleIdToRequirementLevel.put(rule.getContext().getIdProperty().getValue(), requirementLevel);
|
||||
guideline.compilationResult = compilationResult;
|
||||
return guideline;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.dnetlib.validator2.validation.guideline;
|
||||
|
||||
import eu.dnetlib.validator2.engine.Rule;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
interface SyntheticRule<T> extends Rule<T> {
|
||||
|
||||
SyntheticRule<T> parentRule();
|
||||
|
||||
Rule<Document> applicabilityRule();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue