430 lines
18 KiB
Java
430 lines
18 KiB
Java
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");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|