/** * This method is used by the isUPAConform method to check whether @p term and @p otherTerm * are the same resp. match each other. */ static bool termMatches(const XsdTerm::Ptr &term, const XsdTerm::Ptr &otherTerm, const NamePool::Ptr &namePool) { if (term->isElement()) { const XsdElement::Ptr element(term); if (otherTerm->isElement()) { // both, the term and the other term are elements const XsdElement::Ptr otherElement(otherTerm); // if they have the same name they match if (element->name(namePool) == otherElement->name(namePool)) return true; } else if (otherTerm->isWildcard()) { // the term is an element and the other term a wildcard const XsdWildcard::Ptr wildcard(otherTerm); // wildcards using XsdWildcard::absentNamespace, so we have to fix that here QXmlName name = element->name(namePool); if (name.namespaceURI() == StandardNamespaces::empty) name.setNamespaceURI(namePool->allocateNamespace(XsdWildcard::absentNamespace())); // if the wildcards namespace constraint allows the elements name, they match if (XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard, namePool)) return true; } } else if (term->isWildcard()) { const XsdWildcard::Ptr wildcard(term); if (otherTerm->isElement()) { // the term is a wildcard and the other term an element const XsdElement::Ptr otherElement(otherTerm); // wildcards using XsdWildcard::absentNamespace, so we have to fix that here QXmlName name = otherElement->name(namePool); if (name.namespaceURI() == StandardNamespaces::empty) name.setNamespaceURI(namePool->allocateNamespace(XsdWildcard::absentNamespace())); // if the wildcards namespace constraint allows the elements name, they match if (XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard, namePool)) return true; } else if (otherTerm->isWildcard()) { // both, the term and the other term are wildcards const XsdWildcard::Ptr otherWildcard(otherTerm); // check if the range of the wildcard overlaps. const XsdWildcard::Ptr intersectionWildcard = XsdSchemaHelper::wildcardIntersection(wildcard, otherWildcard); if (!intersectionWildcard || (intersectionWildcard && !(intersectionWildcard->namespaceConstraint()->variety() != XsdWildcard::NamespaceConstraint::Not && intersectionWildcard->namespaceConstraint()->namespaces().isEmpty()))) return true; } } return false; }
/** * This method is used by the subsumes algorithm to check whether the @p derivedTerm is validly derived from the @p baseTerm. * * @param baseTerm The term of the base component (type or group). * @param derivedTerm The term of the derived component (type or group). * @param particles A hash to map the passed base and derived term to the particles they belong to. * @param context The schema context. * @param errorMsg The error message in the case that an error occurs. */ static bool derivedTermValid(const XsdTerm::Ptr &baseTerm, const XsdTerm::Ptr &derivedTerm, const QHash<XsdTerm::Ptr, XsdParticle::Ptr> &particles, const XsdSchemaContext::Ptr &context, QString &errorMsg) { const NamePool::Ptr namePool(context->namePool()); // find the particles where the base and derived term belongs to const XsdParticle::Ptr baseParticle = particles.value(baseTerm); const XsdParticle::Ptr derivedParticle = particles.value(derivedTerm); // check that an empty particle can not be derived from a non-empty particle if (derivedParticle && baseParticle) { if (XsdSchemaHelper::isParticleEmptiable(derivedParticle) && !XsdSchemaHelper::isParticleEmptiable(baseParticle)) { errorMsg = QtXmlPatterns::tr("Empty particle cannot be derived from non-empty particle."); return false; } } if (baseTerm->isElement()) { const XsdElement::Ptr element(baseTerm); if (derivedTerm->isElement()) { // if both terms are elements const XsdElement::Ptr derivedElement(derivedTerm); // check names are equal if (element->name(namePool) != derivedElement->name(namePool)) { errorMsg = QtXmlPatterns::tr("Derived particle is missing element %1.").arg(formatKeyword(element->displayName(namePool))); return false; } // check value constraints are equal (if available) if (element->valueConstraint() && element->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) { if (!derivedElement->valueConstraint()) { errorMsg = QtXmlPatterns::tr("Derived element %1 is missing value constraint as defined in base particle.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } if (derivedElement->valueConstraint()->variety() != XsdElement::ValueConstraint::Fixed) { errorMsg = QtXmlPatterns::tr("Derived element %1 has weaker value constraint than base particle.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } const QSourceLocation dummyLocation(QUrl(QLatin1String("http://dummy.org")), 1, 1); const XsdTypeChecker checker(context, QVector<QXmlName>(), dummyLocation); if (!checker.valuesAreEqual(element->valueConstraint()->value(), derivedElement->valueConstraint()->value(), derivedElement->type())) { errorMsg = QtXmlPatterns::tr("Fixed value constraint of element %1 differs from value constraint in base particle.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } } // check that a derived element can not be nillable if the base element is not nillable if (!element->isNillable() && derivedElement->isNillable()) { errorMsg = QtXmlPatterns::tr("Derived element %1 cannot be nillable as base element is not nillable.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } // check that the constraints of the derived element are more strict then the constraints of the base element const XsdElement::BlockingConstraints baseConstraints = element->disallowedSubstitutions(); const XsdElement::BlockingConstraints derivedConstraints = derivedElement->disallowedSubstitutions(); if (((baseConstraints & XsdElement::RestrictionConstraint) && !(derivedConstraints & XsdElement::RestrictionConstraint)) || ((baseConstraints & XsdElement::ExtensionConstraint) && !(derivedConstraints & XsdElement::ExtensionConstraint)) || ((baseConstraints & XsdElement::SubstitutionConstraint) && !(derivedConstraints & XsdElement::SubstitutionConstraint))) { errorMsg = QtXmlPatterns::tr("Block constraints of derived element %1 must not be more weaker than in the base element.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } // if the type of both elements is the same we can stop testing here if (element->type()->name(namePool) == derivedElement->type()->name(namePool)) return true; // check that the type of the derived element can validly derived from the type of the base element if (derivedElement->type()->isSimpleType()) { if (!XsdSchemaHelper::isSimpleDerivationOk(derivedElement->type(), element->type(), SchemaType::DerivationConstraints())) { errorMsg = QtXmlPatterns::tr("Simple type of derived element %1 cannot be validly derived from base element.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } } else if (derivedElement->type()->isComplexType()) { if (!XsdSchemaHelper::isComplexDerivationOk(derivedElement->type(), element->type(), SchemaType::DerivationConstraints())) { errorMsg = QtXmlPatterns::tr("Complex type of derived element %1 cannot be validly derived from base element.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } } // if both, derived and base element, have a complex type that contains a particle itself, apply the subsumes algorithm // recursive on their particles if (element->type()->isComplexType() && derivedElement->type()->isComplexType()) { if (element->type()->isDefinedBySchema() && derivedElement->type()->isDefinedBySchema()) { const XsdComplexType::Ptr baseType(element->type()); const XsdComplexType::Ptr derivedType(derivedElement->type()); if ((baseType->contentType()->variety() == XsdComplexType::ContentType::ElementOnly || baseType->contentType()->variety() == XsdComplexType::ContentType::Mixed) && (derivedType->contentType()->variety() == XsdComplexType::ContentType::ElementOnly || derivedType->contentType()->variety() == XsdComplexType::ContentType::Mixed)) { return XsdParticleChecker::subsumes(baseType->contentType()->particle(), derivedType->contentType()->particle(), context, errorMsg); } } } return true; } else if (derivedTerm->isWildcard()) { // derive a wildcard from an element is not allowed errorMsg = QtXmlPatterns::tr("Element %1 is missing in derived particle.").arg(formatKeyword(element->displayName(namePool))); return false; } } else if (baseTerm->isWildcard()) { const XsdWildcard::Ptr wildcard(baseTerm); if (derivedTerm->isElement()) { // the base term is a wildcard and derived term an element const XsdElement::Ptr derivedElement(derivedTerm); // wildcards using XsdWildcard::absentNamespace, so we have to fix that here QXmlName name = derivedElement->name(namePool); if (name.namespaceURI() == StandardNamespaces::empty) name.setNamespaceURI(namePool->allocateNamespace(XsdWildcard::absentNamespace())); // check that name of the element is allowed by the wildcards namespace constraint if (!XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard, namePool)) { errorMsg = QtXmlPatterns::tr("Element %1 does not match namespace constraint of wildcard in base particle.").arg(formatKeyword(derivedElement->displayName(namePool))); return false; } } else if (derivedTerm->isWildcard()) { // both, derived and base term are wildcards const XsdWildcard::Ptr derivedWildcard(derivedTerm); // check that the derived wildcard is a valid subset of the base wildcard if (!XsdSchemaHelper::isWildcardSubset(derivedWildcard, wildcard)) { errorMsg = QtXmlPatterns::tr("Wildcard in derived particle is not a valid subset of wildcard in base particle."); return false; } if (!XsdSchemaHelper::checkWildcardProcessContents(wildcard, derivedWildcard)) { errorMsg = QtXmlPatterns::tr("processContent of wildcard in derived particle is weaker than wildcard in base particle."); return false; } } return true; } return false; }