/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtXmlPatterns module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qxsdschemahelper_p.h" #include "qbuiltintypes_p.h" #include "qvaluefactory_p.h" #include "qxsdcomplextype_p.h" #include "qxsdmodelgroup_p.h" #include "qxsdsimpletype_p.h" #include "qxsdtypechecker_p.h" QT_BEGIN_NAMESPACE using namespace QPatternist; /* * Calculates the effective total range minimum of the given @p particle as * described by the algorithm in the schema spec. */ static inline unsigned int effectiveTotalRangeMinimum(const XsdParticle::Ptr &particle) { const XsdModelGroup::Ptr group = particle->term(); if (group->compositor() == XsdModelGroup::ChoiceCompositor) { // @see http://www.w3.org/TR/xmlschema11-1/# cos-choice-range int minValue = -1; const XsdParticle::List particles = group->particles(); if (particles.isEmpty()) minValue = 0; for (int i = 0; i < particles.count(); ++i) { const XsdParticle::Ptr particle = particles.at(i); if (particle->term()->isElement() || particle->term()->isWildcard()) { if (minValue == -1) { minValue = particle->minimumOccurs(); } else { minValue = qMin((unsigned int)minValue, particle->minimumOccurs()); } } else if (particle->term()->isModelGroup()) { if (minValue == -1) { minValue = effectiveTotalRangeMinimum(particle); } else { minValue = qMin((unsigned int)minValue, effectiveTotalRangeMinimum(particle)); } } } return (particle->minimumOccurs() * minValue); } else { // @see http://www.w3.org/TR/xmlschema11-1/# cos-seq-range unsigned int sum = 0; const XsdParticle::List particles = group->particles(); for (int i = 0; i < particles.count(); ++i) { const XsdParticle::Ptr particle = particles.at(i); if (particle->term()->isElement() || particle->term()->isWildcard()) sum += particle->minimumOccurs(); else if (particle->term()->isModelGroup()) sum += effectiveTotalRangeMinimum(particle); } return (particle->minimumOccurs() * sum); } } bool XsdSchemaHelper::isParticleEmptiable(const XsdParticle::Ptr &particle) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-group-emptiable if (particle->minimumOccurs() == 0) return true; if (!(particle->term()->isModelGroup())) return false; return (effectiveTotalRangeMinimum(particle) == 0); } bool XsdSchemaHelper::wildcardAllowsNamespaceName(const QString &nameSpace, const XsdWildcard::NamespaceConstraint::Ptr &constraint) { // @see http://www.w3.org/TR/xmlschema11-1/#cvc-wildcard-namespace // 1 if (constraint->variety() == XsdWildcard::NamespaceConstraint::Any) return true; // 2 if (constraint->variety() == XsdWildcard::NamespaceConstraint::Not) { // 2.1 if (!constraint->namespaces().contains(nameSpace)) // 2.2 if (nameSpace != XsdWildcard::absentNamespace()) // 2.3 return true; } // 3 if (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) { if (constraint->namespaces().contains(nameSpace)) return true; } return false; } bool XsdSchemaHelper::wildcardAllowsExpandedName(const QXmlName &name, const XsdWildcard::Ptr &wildcard, const NamePool::Ptr &namePool) { // @see http://www.w3.org/TR/xmlschema11-1/#cvc-wildcard-name // 1 if (!wildcardAllowsNamespaceName(namePool->stringForNamespace(name.namespaceURI()), wildcard->namespaceConstraint())) return false; // 2, 3, 4 //TODO: we have no disallowed namespace yet return true; } bool XsdSchemaHelper::isWildcardSubset(const XsdWildcard::Ptr &wildcard, const XsdWildcard::Ptr &otherWildcard) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-ns-subset // wildcard =^ sub // otherWildcard =^ super const XsdWildcard::NamespaceConstraint::Ptr constraint(wildcard->namespaceConstraint()); const XsdWildcard::NamespaceConstraint::Ptr otherConstraint(otherWildcard->namespaceConstraint()); // 1 if (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Any) return true; // 2 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { if (otherConstraint->namespaces().contains(constraint->namespaces())) return true; } // 3 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) { if (!constraint->namespaces().intersects(otherConstraint->namespaces())) return true; } // 4 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) { if (constraint->namespaces().contains(otherConstraint->namespaces())) return true; } return false; } XsdWildcard::Ptr XsdSchemaHelper::wildcardUnion(const XsdWildcard::Ptr &wildcard, const XsdWildcard::Ptr &otherWildcard) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-aw-union XsdWildcard::Ptr unionWildcard(new XsdWildcard()); const XsdWildcard::NamespaceConstraint::Ptr constraint(wildcard->namespaceConstraint()); const XsdWildcard::NamespaceConstraint::Ptr otherConstraint(otherWildcard->namespaceConstraint()); // 1 if ((constraint->variety() == otherConstraint->variety()) && (constraint->namespaces() == otherConstraint->namespaces())) { unionWildcard->namespaceConstraint()->setVariety(constraint->variety()); unionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces()); return unionWildcard; } // 2 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Any) || (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Any)) { unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any); return unionWildcard; } // 3 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration); unionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces() + otherConstraint->namespaces()); return unionWildcard; } // 4 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) { if (constraint->namespaces() != otherConstraint->namespaces()) { unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not); unionWildcard->namespaceConstraint()->setNamespaces(QSet() << XsdWildcard::absentNamespace()); return unionWildcard; } } // 5 QSet sSet, negatedSet; bool matches5 = false; if (((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && !constraint->namespaces().contains(XsdWildcard::absentNamespace())) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { negatedSet = constraint->namespaces(); sSet = otherConstraint->namespaces(); matches5 = true; } else if (((otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not) && !otherConstraint->namespaces().contains(XsdWildcard::absentNamespace())) && (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { negatedSet = otherConstraint->namespaces(); sSet = constraint->namespaces(); matches5 = true; } if (matches5) { if (sSet.contains(negatedSet.values().first()) && sSet.contains(XsdWildcard::absentNamespace())) { // 5.1 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any); return unionWildcard; } if (sSet.contains(negatedSet.values().first()) && !sSet.contains(XsdWildcard::absentNamespace())) { // 5.2 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not); unionWildcard->namespaceConstraint()->setNamespaces(QSet() << XsdWildcard::absentNamespace()); return unionWildcard; } if (!sSet.contains(negatedSet.values().first()) && sSet.contains(XsdWildcard::absentNamespace())) { // 5.3 return XsdWildcard::Ptr(); // not expressible } if (!sSet.contains(negatedSet.values().first()) && !sSet.contains(XsdWildcard::absentNamespace())) { // 5.4 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not); unionWildcard->namespaceConstraint()->setNamespaces(negatedSet); return unionWildcard; } } // 6 bool matches6 = false; if (((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && constraint->namespaces().contains(XsdWildcard::absentNamespace())) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { negatedSet = constraint->namespaces(); sSet = otherConstraint->namespaces(); matches6 = true; } else if (((otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not) && otherConstraint->namespaces().contains(XsdWildcard::absentNamespace())) && (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { negatedSet = otherConstraint->namespaces(); sSet = constraint->namespaces(); matches6 = true; } if (matches6) { if (sSet.contains(XsdWildcard::absentNamespace())) { // 6.1 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any); return unionWildcard; } if (!sSet.contains(XsdWildcard::absentNamespace())) { // 6.2 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not); unionWildcard->namespaceConstraint()->setNamespaces(QSet() += XsdWildcard::absentNamespace()); return unionWildcard; } } return XsdWildcard::Ptr(); } XsdWildcard::Ptr XsdSchemaHelper::wildcardIntersection(const XsdWildcard::Ptr &wildcard, const XsdWildcard::Ptr &otherWildcard) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-aw-intersect const XsdWildcard::NamespaceConstraint::Ptr constraint(wildcard->namespaceConstraint()); const XsdWildcard::NamespaceConstraint::Ptr otherConstraint(otherWildcard->namespaceConstraint()); const XsdWildcard::Ptr intersectionWildcard(new XsdWildcard()); // 1 if ((constraint->variety() == otherConstraint->variety()) && (constraint->namespaces() == otherConstraint->namespaces())) { intersectionWildcard->namespaceConstraint()->setVariety(constraint->variety()); intersectionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces()); return intersectionWildcard; } // 2 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Any) && (otherConstraint->variety() != XsdWildcard::NamespaceConstraint::Any)) { intersectionWildcard->namespaceConstraint()->setVariety(otherConstraint->variety()); intersectionWildcard->namespaceConstraint()->setNamespaces(otherConstraint->namespaces()); return intersectionWildcard; } // 2 if ((constraint->variety() != XsdWildcard::NamespaceConstraint::Any) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Any)) { intersectionWildcard->namespaceConstraint()->setVariety(constraint->variety()); intersectionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces()); return intersectionWildcard; } // 3 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { QSet set = otherConstraint->namespaces(); set.subtract(constraint->namespaces()); set.remove(XsdWildcard::absentNamespace()); intersectionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration); intersectionWildcard->namespaceConstraint()->setNamespaces(set); return intersectionWildcard; } // 3 if ((otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { QSet set = constraint->namespaces(); set.subtract(otherConstraint->namespaces()); set.remove(XsdWildcard::absentNamespace()); intersectionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration); intersectionWildcard->namespaceConstraint()->setNamespaces(set); return intersectionWildcard; } // 4 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) { QSet set = constraint->namespaces(); set.intersect(otherConstraint->namespaces()); intersectionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration); intersectionWildcard->namespaceConstraint()->setNamespaces(set); return intersectionWildcard; } // 6 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) { if (!(constraint->namespaces().contains(XsdWildcard::absentNamespace())) && otherConstraint->namespaces().contains(XsdWildcard::absentNamespace())) { return wildcard; } if (constraint->namespaces().contains(XsdWildcard::absentNamespace()) && !(otherConstraint->namespaces().contains(XsdWildcard::absentNamespace()))) { return otherWildcard; } } // 5 as not expressible return empty wildcard return XsdWildcard::Ptr(); } static SchemaType::DerivationConstraints convertBlockingConstraints(const NamedSchemaComponent::BlockingConstraints &constraints) { SchemaType::DerivationConstraints result = 0; if (constraints & NamedSchemaComponent::RestrictionConstraint) result |= SchemaType::RestrictionConstraint; if (constraints & NamedSchemaComponent::ExtensionConstraint) result |= SchemaType::ExtensionConstraint; return result; } bool XsdSchemaHelper::isValidlySubstitutable(const SchemaType::Ptr &type, const SchemaType::Ptr &otherType, const SchemaType::DerivationConstraints &constraints) { // @see http://www.w3.org/TR/xmlschema11-1/#key-val-sub-type // 1 if (type->isComplexType() && otherType->isComplexType()) { SchemaType::DerivationConstraints keywords = constraints; if (otherType->isDefinedBySchema()) keywords |= convertBlockingConstraints(XsdComplexType::Ptr(otherType)->prohibitedSubstitutions()); return isComplexDerivationOk(type, otherType, keywords); } // 2 if (type->isComplexType() && otherType->isSimpleType()) { return isComplexDerivationOk(type, otherType, constraints); } // 3 if (type->isSimpleType() && otherType->isSimpleType()) { return isSimpleDerivationOk(type, otherType, constraints); } return false; } bool XsdSchemaHelper::isSimpleDerivationOk(const SchemaType::Ptr &derivedType, const SchemaType::Ptr &baseType, const SchemaType::DerivationConstraints &constraints) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-st-derived-ok // 1 if (derivedType == baseType) return true; // 2.1 if ((constraints & SchemaType::RestrictionConstraint) || derivedType->wxsSuperType()->derivationConstraints() & SchemaType::RestrictionConstraint) { return false; } // 2.2.1 if (derivedType->wxsSuperType() == baseType) return true; // 2.2.2 if (derivedType->wxsSuperType() != BuiltinTypes::xsAnyType) { if (isSimpleDerivationOk(derivedType->wxsSuperType(), baseType, constraints)) return true; } // 2.2.3 if (derivedType->category() == SchemaType::SimpleTypeList || derivedType->category() == SchemaType::SimpleTypeUnion) { if (baseType == BuiltinTypes::xsAnySimpleType) return true; } // 2.2.4 if (baseType->category() == SchemaType::SimpleTypeUnion && baseType->isDefinedBySchema()) { // 2.2.4.1 const AnySimpleType::List memberTypes = XsdSimpleType::Ptr(baseType)->memberTypes(); for (int i = 0; i < memberTypes.count(); ++i) { if (isSimpleDerivationOk(derivedType, memberTypes.at(i), constraints)) { // 2.2.4.2 if (XsdSimpleType::Ptr(baseType)->facets().isEmpty()) { // 2.2.4.3 return true; } } } } return false; } bool XsdSchemaHelper::isComplexDerivationOk(const SchemaType::Ptr &derivedType, const SchemaType::Ptr &baseType, const SchemaType::DerivationConstraints &constraints) { if (!derivedType) return false; // @see http://www.w3.org/TR/xmlschema11-1/#cos-ct-derived-ok // 1 if (derivedType != baseType) { if ((derivedType->derivationMethod() == SchemaType::DerivationRestriction) && (constraints & SchemaType::RestrictionConstraint)) return false; if ((derivedType->derivationMethod() == SchemaType::DerivationExtension) && (constraints & SchemaType::ExtensionConstraint)) return false; } // 2.1 if (derivedType == baseType) return true; // 2.2 if (derivedType->wxsSuperType() == baseType) return true; // 2.3 bool isOk = true; if (derivedType->wxsSuperType() == BuiltinTypes::xsAnyType) { // 2.3.1 isOk = false; } else { // 2.3.2 if (!derivedType->wxsSuperType()) return false; if (derivedType->wxsSuperType()->isComplexType()) { // 2.3.2.1 isOk = isComplexDerivationOk(derivedType->wxsSuperType(), baseType, constraints); } else { // 2.3.2.2 isOk = isSimpleDerivationOk(derivedType->wxsSuperType(), baseType, constraints); } } if (isOk) return true; return false; } bool XsdSchemaHelper::constructAndCompare(const DerivedString::Ptr &operand1, const AtomicComparator::Operator op, const DerivedString::Ptr &operand2, const SchemaType::Ptr &type, const ReportContext::Ptr &context, const SourceLocationReflection *const sourceLocationReflection) { Q_ASSERT_X(type->category() == SchemaType::SimpleTypeAtomic, Q_FUNC_INFO, "We can only compare atomic values."); // we can not cast a xs:String to a xs:QName, so lets go the safe way if (type->name(context->namePool()) == BuiltinTypes::xsQName->name(context->namePool())) return false; const AtomicValue::Ptr value1 = ValueFactory::fromLexical(operand1->stringValue(), type, context, sourceLocationReflection); if (value1->hasError()) return false; const AtomicValue::Ptr value2 = ValueFactory::fromLexical(operand2->stringValue(), type, context, sourceLocationReflection); if (value2->hasError()) return false; return ComparisonFactory::compare(value1, op, value2, type, context, sourceLocationReflection); } bool XsdSchemaHelper::checkWildcardProcessContents(const XsdWildcard::Ptr &baseWildcard, const XsdWildcard::Ptr &derivedWildcard) { if (baseWildcard->processContents() == XsdWildcard::Strict) { if (derivedWildcard->processContents() == XsdWildcard::Lax || derivedWildcard->processContents() == XsdWildcard::Skip) { return false; } } else if (baseWildcard->processContents() == XsdWildcard::Lax) { if (derivedWildcard->processContents() == XsdWildcard::Skip) return false; } return true; } bool XsdSchemaHelper::foundSubstitutionGroupTransitive(const XsdElement::Ptr &head, const XsdElement::Ptr &member, QSet &visitedElements) { if (visitedElements.contains(member)) return false; else visitedElements.insert(member); if (member->substitutionGroupAffiliations().isEmpty()) return false; if (member->substitutionGroupAffiliations().contains(head)) { return true; } else { const XsdElement::List affiliations = member->substitutionGroupAffiliations(); for (int i = 0; i < affiliations.count(); ++i) { if (foundSubstitutionGroupTransitive(head, affiliations.at(i), visitedElements)) return true; } return false; } } void XsdSchemaHelper::foundSubstitutionGroupTypeInheritance(const SchemaType::Ptr &headType, const SchemaType::Ptr &memberType, QSet &derivationSet, NamedSchemaComponent::BlockingConstraints &blockSet) { if (!memberType) return; if (memberType == headType) return; derivationSet.insert(memberType->derivationMethod()); if (memberType->isComplexType()) { const XsdComplexType::Ptr complexType(memberType); blockSet |= complexType->prohibitedSubstitutions(); } foundSubstitutionGroupTypeInheritance(headType, memberType->wxsSuperType(), derivationSet, blockSet); } bool XsdSchemaHelper::substitutionGroupOkTransitive(const XsdElement::Ptr &head, const XsdElement::Ptr &member, const NamePool::Ptr &namePool) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-equiv-derived-ok-rec // 1 if ((member->name(namePool) == head->name(namePool)) && (member->type() == head->type())) return true; // 2.1 if (head->disallowedSubstitutions() & NamedSchemaComponent::SubstitutionConstraint) return false; // 2.2 { QSet visitedElements; if (!foundSubstitutionGroupTransitive(head, member, visitedElements)) return false; } // 2.3 { QSet derivationSet; NamedSchemaComponent::BlockingConstraints blockSet; foundSubstitutionGroupTypeInheritance(head->type(), member->type(), derivationSet, blockSet); NamedSchemaComponent::BlockingConstraints checkSet(blockSet); checkSet |= head->disallowedSubstitutions(); if (head->type()->isComplexType() && head->type()->isDefinedBySchema()) { const XsdComplexType::Ptr complexType(head->type()); checkSet |= complexType->prohibitedSubstitutions(); } if ((checkSet & NamedSchemaComponent::RestrictionConstraint) && derivationSet.contains(SchemaType::DerivationRestriction)) return false; if ((checkSet & NamedSchemaComponent::ExtensionConstraint) && derivationSet.contains(SchemaType::DerivationExtension)) return false; if (checkSet & NamedSchemaComponent::SubstitutionConstraint) return false; } return true; } bool XsdSchemaHelper::isValidAttributeGroupRestriction(const XsdAttributeGroup::Ptr &derivedAttributeGroup, const XsdAttributeGroup::Ptr &attributeGroup, const XsdSchemaContext::Ptr &context, QString &errorMsg) { // @see http://www.w3.org/TR/xmlschema-1/#derivation-ok-restriction const XsdAttributeUse::List derivedAttributeUses = derivedAttributeGroup->attributeUses(); const XsdAttributeUse::List baseAttributeUses = attributeGroup->attributeUses(); return isValidAttributeUsesRestriction(derivedAttributeUses, baseAttributeUses, derivedAttributeGroup->wildcard(), attributeGroup->wildcard(), context, errorMsg); } bool XsdSchemaHelper::isValidAttributeUsesRestriction(const XsdAttributeUse::List &derivedAttributeUses, const XsdAttributeUse::List &baseAttributeUses, const XsdWildcard::Ptr &derivedWildcard, const XsdWildcard::Ptr &wildcard, const XsdSchemaContext::Ptr &context, QString &errorMsg) { const NamePool::Ptr namePool(context->namePool()); QHash baseAttributeUsesLookup; for (int i = 0; i < baseAttributeUses.count(); ++i) baseAttributeUsesLookup.insert(baseAttributeUses.at(i)->attribute()->name(namePool), baseAttributeUses.at(i)); QHash derivedAttributeUsesLookup; for (int i = 0; i < derivedAttributeUses.count(); ++i) derivedAttributeUsesLookup.insert(derivedAttributeUses.at(i)->attribute()->name(namePool), derivedAttributeUses.at(i)); // 2 for (int i = 0; i < derivedAttributeUses.count(); ++i) { const XsdAttributeUse::Ptr derivedAttributeUse = derivedAttributeUses.at(i); // prohibited attributes are no real attributes, so skip them in that test here if (derivedAttributeUse->useType() == XsdAttributeUse::ProhibitedUse) continue; if (baseAttributeUsesLookup.contains(derivedAttributeUse->attribute()->name(namePool))) { const XsdAttributeUse::Ptr baseAttributeUse(baseAttributeUsesLookup.value(derivedAttributeUse->attribute()->name(namePool))); // 2.1.1 if (baseAttributeUse->isRequired() == true && derivedAttributeUse->isRequired() == false) { errorMsg = QtXmlPatterns::tr("Base attribute %1 is required but derived attribute is not.").arg(formatAttribute(baseAttributeUse->attribute()->displayName(namePool))); return false; } // 2.1.2 if (!isSimpleDerivationOk(derivedAttributeUse->attribute()->type(), baseAttributeUse->attribute()->type(), SchemaType::DerivationConstraints())) { errorMsg = QtXmlPatterns::tr("Type of derived attribute %1 cannot be validly derived from type of base attribute.").arg(formatAttribute(derivedAttributeUse->attribute()->displayName(namePool))); return false; } // 2.1.3 XsdAttributeUse::ValueConstraint::Ptr derivedConstraint; if (derivedAttributeUse->valueConstraint()) derivedConstraint = derivedAttributeUse->valueConstraint(); else if (derivedAttributeUse->attribute()->valueConstraint()) derivedConstraint = XsdAttributeUse::ValueConstraint::fromAttributeValueConstraint(derivedAttributeUse->attribute()->valueConstraint()); XsdAttributeUse::ValueConstraint::Ptr baseConstraint; if (baseAttributeUse->valueConstraint()) baseConstraint = baseAttributeUse->valueConstraint(); else if (baseAttributeUse->attribute()->valueConstraint()) baseConstraint = XsdAttributeUse::ValueConstraint::fromAttributeValueConstraint(baseAttributeUse->attribute()->valueConstraint()); bool ok = false; if (!baseConstraint || baseConstraint->variety() == XsdAttributeUse::ValueConstraint::Default) ok = true; if (derivedConstraint && baseConstraint) { const XsdTypeChecker checker(context, QVector(), QSourceLocation(QUrl(QLatin1String("http://dummy.org")), 1, 1)); if (derivedConstraint->variety() == XsdAttributeUse::ValueConstraint::Fixed && checker.valuesAreEqual(derivedConstraint->value(), baseConstraint->value(), baseAttributeUse->attribute()->type())) ok = true; } if (!ok) { errorMsg = QtXmlPatterns::tr("Value constraint of derived attribute %1 does not match value constraint of base attribute.").arg(formatAttribute(derivedAttributeUse->attribute()->displayName(namePool))); return false; } } else { if (!wildcard) { errorMsg = QtXmlPatterns::tr("Derived attribute %1 does not exist in the base definition.").arg(formatAttribute(derivedAttributeUse->attribute()->displayName(namePool))); return false; } QXmlName name = derivedAttributeUse->attribute()->name(namePool); // wildcards using XsdWildcard::absentNamespace, so we have to fix that here if (name.namespaceURI() == StandardNamespaces::empty) name.setNamespaceURI(namePool->allocateNamespace(XsdWildcard::absentNamespace())); if (!wildcardAllowsExpandedName(name, wildcard, namePool)) { errorMsg = QtXmlPatterns::tr("Derived attribute %1 does not match the wildcard in the base definition.").arg(formatAttribute(derivedAttributeUse->attribute()->displayName(namePool))); return false; } } } // 3 for (int i = 0; i < baseAttributeUses.count(); ++i) { const XsdAttributeUse::Ptr baseAttributeUse = baseAttributeUses.at(i); if (baseAttributeUse->isRequired()) { if (derivedAttributeUsesLookup.contains(baseAttributeUse->attribute()->name(namePool))) { if (!derivedAttributeUsesLookup.value(baseAttributeUse->attribute()->name(namePool))->isRequired()) { errorMsg = QtXmlPatterns::tr("Base attribute %1 is required but derived attribute is not.").arg(formatAttribute(baseAttributeUse->attribute()->displayName(namePool))); return false; } } else { errorMsg = QtXmlPatterns::tr("Base attribute %1 is required but missing in derived definition.").arg(formatAttribute(baseAttributeUse->attribute()->displayName(namePool))); return false; } } } // 4 if (derivedWildcard) { if (!wildcard) { errorMsg = QtXmlPatterns::tr("Derived definition contains an %1 element that does not exists in the base definition").arg(formatElement("anyAttribute.")); return false; } if (!isWildcardSubset(derivedWildcard, wildcard)) { errorMsg = QtXmlPatterns::tr("Derived wildcard is not a subset of the base wildcard."); return false; } if (!checkWildcardProcessContents(wildcard, derivedWildcard)) { errorMsg = QtXmlPatterns::tr("%1 of derived wildcard is not a valid restriction of %2 of base wildcard").arg(formatKeyword("processContents")).arg(formatKeyword("processContents.")); return false; } } return true; } bool XsdSchemaHelper::isValidAttributeUsesExtension(const XsdAttributeUse::List &derivedAttributeUses, const XsdAttributeUse::List &attributeUses, const XsdWildcard::Ptr &derivedWildcard, const XsdWildcard::Ptr &wildcard, const XsdSchemaContext::Ptr &context, QString &errorMsg) { // @see http://www.w3.org/TR/xmlschema11-1/#cos-ct-extends const NamePool::Ptr namePool(context->namePool()); // 1.2 QHash lookupHash; for (int i = 0; i < derivedAttributeUses.count(); ++i) lookupHash.insert(derivedAttributeUses.at(i)->attribute()->name(namePool), derivedAttributeUses.at(i)->attribute()); for (int i = 0; i < attributeUses.count(); ++i) { const QXmlName attributeName = attributeUses.at(i)->attribute()->name(namePool); if (!lookupHash.contains(attributeName)) { errorMsg = QtXmlPatterns::tr("Attribute %1 from base type is missing in derived type.").arg(formatKeyword(namePool->displayName(attributeName))); return false; } if (lookupHash.value(attributeName)->type() != attributeUses.at(i)->attribute()->type()) { errorMsg = QtXmlPatterns::tr("Type of derived attribute %1 differs from type of base attribute.").arg(formatKeyword(namePool->displayName(attributeName))); return false; } } // 1.3 if (wildcard) { if (!derivedWildcard) { errorMsg = QtXmlPatterns::tr("Base definition contains an %1 element that is missing in the derived definition").arg(formatElement("anyAttribute.")); return false; } } return true; } QT_END_NAMESPACE