summaryrefslogtreecommitdiff
path: root/src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commite1b2c9deb5943faae2b29be6a5c006f75bb73f06 (patch)
treefc79e45367c0a8fc71185e9afc33f7503a58653c /src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp
downloadqtxmlpatterns-e1b2c9deb5943faae2b29be6a5c006f75bb73f06.tar.gz
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp')
-rw-r--r--src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp1276
1 files changed, 1276 insertions, 0 deletions
diff --git a/src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp b/src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp
new file mode 100644
index 0000000..622a39f
--- /dev/null
+++ b/src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp
@@ -0,0 +1,1276 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxsdvalidatinginstancereader_p.h"
+
+#include "qabstractdatetime_p.h"
+#include "qacceltreeresourceloader_p.h"
+#include "qbase64binary_p.h"
+#include "qboolean_p.h"
+#include "qcommonnamespaces_p.h"
+#include "qderivedinteger_p.h"
+#include "qduration_p.h"
+#include "qgenericstaticcontext_p.h"
+#include "qhexbinary_p.h"
+#include "qnamespaceresolver_p.h"
+#include "qpatternplatform_p.h"
+#include "qqnamevalue_p.h"
+#include "qsourcelocationreflection_p.h"
+#include "qvaluefactory_p.h"
+#include "qxmlnamepool.h"
+#include "qxmlquery_p.h"
+#include "qxmlschema_p.h"
+#include "qxsdschemahelper_p.h"
+#include "qxsdschemamerger_p.h"
+#include "qxsdstatemachine_p.h"
+#include "qxsdstatemachinebuilder_p.h"
+#include "qxsdtypechecker_p.h"
+
+#include "qxsdschemadebugger_p.h"
+
+#include <QtCore/QFile>
+#include <QtXmlPatterns/QXmlQuery>
+#include <QtXmlPatterns/QXmlResultItems>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+namespace QPatternist
+{
+ template <>
+ template <>
+ bool XsdStateMachine<XsdTerm::Ptr>::inputEqualsTransition<QXmlName>(QXmlName name, XsdTerm::Ptr term) const
+ {
+ if (term->isElement()) {
+ return (XsdElement::Ptr(term)->name(m_namePool) == name);
+ } else if (term->isWildcard()) {
+ // wildcards using XsdWildcard::absentNamespace, so we have to fix that here
+ if (name.namespaceURI() == StandardNamespaces::empty) {
+ name.setNamespaceURI(m_namePool->allocateNamespace(XsdWildcard::absentNamespace()));
+ }
+
+ return XsdSchemaHelper::wildcardAllowsExpandedName(name, XsdWildcard::Ptr(term), m_namePool);
+ }
+
+ return false;
+ }
+}
+
+XsdValidatingInstanceReader::XsdValidatingInstanceReader(XsdValidatedXmlNodeModel *model, const QUrl &documentUri, const XsdSchemaContext::Ptr &context)
+ : XsdInstanceReader(model, context)
+ , m_model(model)
+ , m_namePool(m_context->namePool())
+ , m_xsiNilName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("nil")))
+ , m_xsiTypeName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("type")))
+ , m_xsiSchemaLocationName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("schemaLocation")))
+ , m_xsiNoNamespaceSchemaLocationName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("noNamespaceSchemaLocation")))
+ , m_documentUri(documentUri)
+{
+ m_idRefsType = m_context->schemaTypeFactory()->createSchemaType(m_namePool->allocateQName(CommonNamespaces::WXS, QLatin1String("IDREFS")));
+}
+
+void XsdValidatingInstanceReader::addSchema(const XsdSchema::Ptr &schema, const QUrl &locationUrl)
+{
+ if (!m_mergedSchemas.contains(locationUrl)) {
+ m_mergedSchemas.insert(locationUrl, QStringList() << schema->targetNamespace());
+ } else {
+ QStringList &targetNamespaces = m_mergedSchemas[locationUrl];
+ if (targetNamespaces.contains(schema->targetNamespace()))
+ return;
+
+ targetNamespaces.append(schema->targetNamespace());
+ }
+
+ const XsdSchemaMerger merger(m_schema, schema);
+ m_schema = merger.mergedSchema();
+/*
+ XsdSchemaDebugger dbg(m_namePool);
+ dbg.dumpSchema(m_schema);
+*/
+}
+
+bool XsdValidatingInstanceReader::read()
+{
+ while (!atEnd()) {
+ readNext();
+
+ if (isEndElement())
+ return true;
+
+ if (isStartElement()) {
+ const QXmlName elementName = name();
+ const QXmlItem currentItem = item();
+ bool hasStateMachine = false;
+ XsdElement::Ptr processedElement;
+
+ if (!validate(hasStateMachine, processedElement))
+ return false;
+
+ read();
+
+ if (processedElement) { // for wildcard with 'skip' we have no element
+ m_model->setAssignedElement(currentItem.toNodeModelIndex(), processedElement);
+
+ // check identity constraints after all child nodes have been
+ // validated, so that we know there assigned types
+ validateIdentityConstraint(processedElement, currentItem);
+ }
+
+ if (!m_stateMachines.isEmpty() && hasStateMachine) {
+ if (!m_stateMachines.top().inEndState()) {
+ error(QtXmlPatterns::tr("Element %1 is missing child element.").arg(formatKeyword(m_namePool->displayName(elementName))));
+ return false;
+ }
+ m_stateMachines.pop();
+ }
+ }
+ }
+
+ // final validations
+
+ // check IDREF occurrences
+ const QStringList ids = m_model->idIdRefBindingIds();
+ QSetIterator<QString> it(m_idRefs);
+ while (it.hasNext()) {
+ const QString id = it.next();
+ if (!ids.contains(id)) {
+ error(QtXmlPatterns::tr("There is one IDREF value with no corresponding ID: %1.").arg(formatKeyword(id)));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void XsdValidatingInstanceReader::error(const QString &msg) const
+{
+ m_context.data()->error(msg, XsdSchemaContext::XSDError, sourceLocation());
+}
+
+bool XsdValidatingInstanceReader::loadSchema(const QString &targetNamespace, const QUrl &location)
+{
+ const AutoPtr<QNetworkReply> reply(AccelTreeResourceLoader::load(location, m_context->networkAccessManager(),
+ m_context, AccelTreeResourceLoader::ContinueOnError));
+ if (!reply)
+ return true;
+
+ // we have to create a separated schema context here, that however shares the type factory
+ XsdSchemaContext::Ptr context(new XsdSchemaContext(m_namePool));
+ context->m_schemaTypeFactory = m_context->m_schemaTypeFactory;
+
+ QXmlSchemaPrivate schema(context);
+ schema.load(reply.data(), location, targetNamespace);
+ if (!schema.isValid()) {
+ error(QtXmlPatterns::tr("Loaded schema file is invalid."));
+ return false;
+ }
+
+ addSchema(schema.m_schemaParserContext->schema(), location);
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validate(bool &hasStateMachine, XsdElement::Ptr &processedElement)
+{
+ // first check if a custom schema is defined
+ if (hasAttribute(m_xsiSchemaLocationName)) {
+ const QString schemaLocation = attribute(m_xsiSchemaLocationName);
+ const QStringList parts = schemaLocation.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ if ((parts.count()%2) == 1) {
+ error(QtXmlPatterns::tr("%1 contains invalid data.").arg(formatKeyword(m_namePool, m_xsiSchemaLocationName)));
+ return false;
+ }
+
+ for (int i = 0; i < parts.count(); i += 2) {
+ const QString identifier = QString::fromLatin1("%1 %2").arg(parts.at(i)).arg(parts.at(i + 1));
+ if (m_processedSchemaLocations.contains(identifier))
+ continue;
+ else
+ m_processedSchemaLocations.insert(identifier);
+
+ // check constraint 4) from http://www.w3.org/TR/xmlschema-1/#schema-loc (only valid for XML Schema 1.0?)
+ if (m_processedNamespaces.contains(parts.at(i))) {
+ error(QtXmlPatterns::tr("xsi:schemaLocation namespace %1 has already appeared earlier in the instance document.").arg(formatKeyword(parts.at(i))));
+ return false;
+ }
+
+ QUrl url(parts.at(i + 1));
+ if (url.isRelative()) {
+ Q_ASSERT(m_documentUri.isValid());
+
+ url = m_documentUri.resolved(url);
+ }
+
+ loadSchema(parts.at(i), url);
+ }
+ }
+
+ if (hasAttribute(m_xsiNoNamespaceSchemaLocationName)) {
+ const QString schemaLocation = attribute(m_xsiNoNamespaceSchemaLocationName);
+
+ if (!m_processedSchemaLocations.contains(schemaLocation)) {
+ m_processedSchemaLocations.insert(schemaLocation);
+
+ if (m_processedNamespaces.contains(QString())) {
+ error(QtXmlPatterns::tr("xsi:noNamespaceSchemaLocation cannot appear after the first no-namespace element or attribute."));
+ return false;
+ }
+
+ QUrl url(schemaLocation);
+ if (url.isRelative()) {
+ Q_ASSERT(m_documentUri.isValid());
+
+ url = m_documentUri.resolved(url);
+ }
+
+ loadSchema(QString(), url);
+ }
+ }
+
+ m_processedNamespaces.insert(m_namePool->stringForNamespace(name().namespaceURI()));
+
+ if (!m_schema) {
+ error(QtXmlPatterns::tr("No schema defined for validation."));
+ return false;
+ }
+
+ // check if we are 'inside' a type definition
+ if (m_stateMachines.isEmpty()) {
+ // find out the type of the top-level element
+ XsdElement::Ptr element = elementByName(name());
+ if (!element) {
+ if (!hasAttribute(m_xsiTypeName)) {
+ error(QtXmlPatterns::tr("No definition for element %1 available.").arg(formatKeyword(m_namePool, name())));
+ return false;
+ }
+
+ // This instance document has an element with no definition in the schema
+ // but an explicitly given type, that is fine according to the spec.
+ // We will create an element definition manually here and continue the
+ // normal validation process
+ element = XsdElement::Ptr(new XsdElement());
+ element->setName(name());
+ element->setIsAbstract(false);
+ element->setIsNillable(hasAttribute(m_xsiNilName));
+
+ const QString type = qNameAttribute(m_xsiTypeName);
+ const QXmlName typeName = convertToQName(type);
+
+ const SchemaType::Ptr elementType = typeByName(typeName);
+ if (!elementType) {
+ error(QtXmlPatterns::tr("Specified type %1 is not known to the schema.").arg(formatType(m_namePool, typeName)));
+ return false;
+ }
+ element->setType(elementType);
+ }
+
+ // rememeber the element we process
+ processedElement = element;
+
+ if (!validateElement(element, hasStateMachine)) {
+ return false;
+ }
+
+ } else {
+ if (!m_stateMachines.top().proceed<QXmlName>(name())) {
+ error(QtXmlPatterns::tr("Element %1 is not defined in this scope.").arg(formatKeyword(m_namePool, name())));
+ return false;
+ }
+
+ const XsdTerm::Ptr term = m_stateMachines.top().lastTransition();
+ if (term->isElement()) {
+ const XsdElement::Ptr element(term);
+
+ // rememeber the element we process
+ processedElement = element;
+
+ if (!validateElement(element, hasStateMachine))
+ return false;
+
+ } else {
+ const XsdWildcard::Ptr wildcard(term);
+ if (wildcard->processContents() != XsdWildcard::Skip) {
+ XsdElement::Ptr elementDeclaration = elementByName(name());
+ if (!elementDeclaration) {
+ if (hasAttribute(m_xsiTypeName)) {
+ // This instance document has an element with no definition in the schema
+ // but an explicitly given type, that is fine according to the spec.
+ // We will create an element definition manually here and continue the
+ // normal validation process
+ elementDeclaration = XsdElement::Ptr(new XsdElement());
+ elementDeclaration->setName(name());
+ elementDeclaration->setIsAbstract(false);
+ elementDeclaration->setIsNillable(hasAttribute(m_xsiNilName));
+
+ const QString type = qNameAttribute(m_xsiTypeName);
+ const QXmlName typeName = convertToQName(type);
+
+ const SchemaType::Ptr elementType = typeByName(typeName);
+ if (!elementType) {
+ error(QtXmlPatterns::tr("Specified type %1 is not known to the schema.").arg(formatType(m_namePool, typeName)));
+ return false;
+ }
+ elementDeclaration->setType(elementType);
+ }
+ }
+
+ if (!elementDeclaration) {
+ if (wildcard->processContents() == XsdWildcard::Strict) {
+ error(QtXmlPatterns::tr("Declaration for element %1 does not exist.").arg(formatKeyword(m_namePool->displayName(name()))));
+ return false;
+ } else {
+ // in this case we put a state machine for the xs:anyType on the statemachine stack,
+ // so we accept every content of this element
+
+ createAndPushStateMachine(anyType()->contentType()->particle());
+ hasStateMachine = true;
+ }
+ } else {
+ if (!validateElement(elementDeclaration, hasStateMachine)) {
+ if (wildcard->processContents() == XsdWildcard::Strict) {
+ error(QtXmlPatterns::tr("Element %1 contains invalid content.").arg(formatKeyword(m_namePool->displayName(name()))));
+ return false;
+ }
+ }
+
+ // rememeber the type of that element node
+ m_model->setAssignedType(item().toNodeModelIndex(), elementDeclaration->type());
+ }
+ } else { // wildcard process contents type is Skip
+ // in this case we put a state machine for the xs:anyType on the statemachine stack,
+ // so we accept every content of this element
+
+ const XsdWildcard::Ptr wildcard(new XsdWildcard());
+ wildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
+ wildcard->setProcessContents(XsdWildcard::Skip);
+
+ const XsdParticle::Ptr outerParticle(new XsdParticle());
+ outerParticle->setMinimumOccurs(1);
+ outerParticle->setMaximumOccurs(1);
+
+ const XsdParticle::Ptr innerParticle(new XsdParticle());
+ innerParticle->setMinimumOccurs(0);
+ innerParticle->setMaximumOccursUnbounded(true);
+ innerParticle->setTerm(wildcard);
+
+ const XsdModelGroup::Ptr outerModelGroup(new XsdModelGroup());
+ outerModelGroup->setCompositor(XsdModelGroup::SequenceCompositor);
+ outerModelGroup->setParticles(XsdParticle::List() << innerParticle);
+ outerParticle->setTerm(outerModelGroup);
+
+ createAndPushStateMachine(outerParticle);
+ hasStateMachine = true;
+ }
+ }
+ }
+
+ return true;
+}
+
+void XsdValidatingInstanceReader::createAndPushStateMachine(const XsdParticle::Ptr &particle)
+{
+ XsdStateMachine<XsdTerm::Ptr> stateMachine(m_namePool);
+
+ XsdStateMachineBuilder builder(&stateMachine, m_namePool, XsdStateMachineBuilder::ValidatingMode);
+ const XsdStateMachine<XsdTerm::Ptr>::StateId endState = builder.reset();
+ const XsdStateMachine<XsdTerm::Ptr>::StateId startState = builder.buildParticle(particle, endState);
+ builder.addStartState(startState);
+
+/*
+ QString fileName = QString("/tmp/foo_%1.dot").arg(m_namePool->displayName(complexType->name(m_namePool)));
+ QString pngFileName = QString("/tmp/foo_%1.png").arg(m_namePool->displayName(complexType->name(m_namePool)));
+ QFile file(fileName);
+ file.open(QIODevice::WriteOnly);
+ stateMachine.outputGraph(&file, "Hello");
+ file.close();
+ ::system(QString("dot -Tpng %1 -o%2").arg(fileName).arg(pngFileName).toLatin1().data());
+*/
+
+ stateMachine = stateMachine.toDFA();
+
+ m_stateMachines.push(stateMachine);
+}
+
+bool XsdValidatingInstanceReader::validateElement(const XsdElement::Ptr &declaration, bool &hasStateMachine)
+{
+ // http://www.w3.org/TR/xmlschema11-1/#d0e10998
+
+ bool isNilled = false;
+
+ // 1 tested already, 'declaration' corresponds D
+
+ // 2
+ if (declaration->isAbstract()) {
+ error(QtXmlPatterns::tr("Element %1 is declared as abstract.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+
+ // 3
+ if (!declaration->isNillable()) {
+ if (hasAttribute(m_xsiNilName)) {
+ error(QtXmlPatterns::tr("Element %1 is not nillable.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false; // 3.1
+ }
+ } else {
+ if (hasAttribute(m_xsiNilName)) {
+ const QString value = attribute(m_xsiNilName);
+ const Boolean::Ptr nil = Boolean::fromLexical(value);
+ if (nil->hasError()) {
+ error(QtXmlPatterns::tr("Attribute %1 contains invalid data: %2").arg(formatKeyword(QLatin1String("nil."))).arg(formatData(value)));
+ return false;
+ }
+
+ // 3.2.3
+ if (nil->as<Boolean>()->value() == true) {
+ // 3.2.3.1
+ if (hasChildElement() || hasChildText()) {
+ error(QtXmlPatterns::tr("Element contains content although it is nillable."));
+ return false;
+ }
+
+ // 3.2.3.2
+ if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
+ error(QtXmlPatterns::tr("Fixed value constraint not allowed if element is nillable."));
+ return false;
+ }
+ }
+
+ isNilled = nil->as<Boolean>()->value();
+ }
+ }
+
+ SchemaType::Ptr finalElementType = declaration->type();
+
+ // 4
+ if (hasAttribute(m_xsiTypeName)) {
+ const QString type = qNameAttribute(m_xsiTypeName);
+ const QXmlName typeName = convertToQName(type);
+
+ const SchemaType::Ptr elementType = typeByName(typeName);
+ // 4.1
+ if (!elementType) {
+ error(QtXmlPatterns::tr("Specified type %1 is not known to the schema.").arg(formatType(m_namePool, typeName)));
+ return false;
+ }
+
+ // 4.2
+ SchemaType::DerivationConstraints constraints = 0;
+ if (declaration->disallowedSubstitutions() & NamedSchemaComponent::ExtensionConstraint)
+ constraints |= SchemaType::ExtensionConstraint;
+ if (declaration->disallowedSubstitutions() & NamedSchemaComponent::RestrictionConstraint)
+ constraints |= SchemaType::RestrictionConstraint;
+
+ if (!XsdSchemaHelper::isValidlySubstitutable(elementType, declaration->type(), constraints)) {
+ if (declaration->type()->name(m_namePool) != BuiltinTypes::xsAnyType->name(m_namePool)) { // xs:anyType is a valid substitutable type here
+ error(QtXmlPatterns::tr("Specified type %1 is not validly substitutable with element type %2.").arg(formatType(m_namePool, elementType)).arg(formatType(m_namePool, declaration->type())));
+ return false;
+ }
+ }
+
+ finalElementType = elementType;
+ }
+
+ if (!validateElementType(declaration, finalElementType, isNilled, hasStateMachine))
+ return false;
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validateElementType(const XsdElement::Ptr &declaration, const SchemaType::Ptr &type, bool isNilled, bool &hasStateMachine)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#d0e11749
+
+ // 1 checked already
+
+ // 2
+ if (type->isComplexType() && type->isDefinedBySchema()) {
+ if (XsdComplexType::Ptr(type)->isAbstract()) {
+ error(QtXmlPatterns::tr("Complex type %1 is not allowed to be abstract.").arg(formatType(m_namePool, type)));
+ return false;
+ }
+ }
+
+ // 3
+ if (type->isSimpleType())
+ return validateElementSimpleType(declaration, type, isNilled); // 3.1
+ else
+ return validateElementComplexType(declaration, type, isNilled, hasStateMachine); // 3.2
+}
+
+bool XsdValidatingInstanceReader::validateElementSimpleType(const XsdElement::Ptr &declaration, const SchemaType::Ptr &type, bool isNilled)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#d0e11749
+
+ // 3.1.1
+ const QSet<QXmlName> allowedAttributes(QSet<QXmlName>() << m_xsiNilName << m_xsiTypeName << m_xsiSchemaLocationName << m_xsiNoNamespaceSchemaLocationName);
+ QSet<QXmlName> elementAttributes = attributeNames();
+ elementAttributes.subtract(allowedAttributes);
+ if (!elementAttributes.isEmpty()) {
+ error(QtXmlPatterns::tr("Element %1 contains not allowed attributes.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+
+ // 3.1.2
+ if (hasChildElement()) {
+ error(QtXmlPatterns::tr("Element %1 contains not allowed child element.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+
+ // 3.1.3
+ if (!isNilled) {
+ const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type, m_context);
+
+ QString actualValue;
+ if (hasChildText()) {
+ actualValue = XsdTypeChecker::normalizedValue(text(), facets);
+ } else {
+ if (declaration->valueConstraint())
+ actualValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
+ }
+
+ QString errorMsg;
+ AnySimpleType::Ptr boundType;
+
+ const XsdTypeChecker checker(m_context, namespaceBindings(item().toNodeModelIndex()), sourceLocation());
+ if (!checker.isValidString(actualValue, type, errorMsg, &boundType)) {
+ error(QtXmlPatterns::tr("Content of element %1 does not match its type definition: %2.").arg(formatKeyword(declaration->displayName(m_namePool))).arg(errorMsg));
+ return false;
+ }
+
+ // additional check
+ if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
+ const QString actualConstraintValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
+ if (!text().isEmpty() && !checker.valuesAreEqual(actualValue, actualConstraintValue, type)) {
+ error(QtXmlPatterns::tr("Content of element %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+ }
+
+ // 4 checked in validateElement already
+
+ // rememeber the type of that element node
+ m_model->setAssignedType(item().toNodeModelIndex(), type);
+
+ const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type, m_context);
+ const QString actualValue = XsdTypeChecker::normalizedValue(text(), facets);
+
+ if (BuiltinTypes::xsID->wxsTypeMatches(type)) {
+ addIdIdRefBinding(actualValue, declaration);
+ }
+
+ if (m_idRefsType->wxsTypeMatches(type)) {
+ const QStringList idRefs = actualValue.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ for (int i = 0; i < idRefs.count(); ++i) {
+ m_idRefs.insert(idRefs.at(i));
+ }
+ } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(type)) {
+ m_idRefs.insert(actualValue);
+ }
+
+ return true;
+}
+
+static bool hasIDAttributeUse(const XsdAttributeUse::List &uses)
+{
+ const int count = uses.count();
+ for (int i = 0; i < count; ++i) {
+ if (BuiltinTypes::xsID->wxsTypeMatches(uses.at(i)->attribute()->type()))
+ return true;
+ }
+
+ return false;
+}
+
+bool XsdValidatingInstanceReader::validateElementComplexType(const XsdElement::Ptr &declaration, const SchemaType::Ptr &type, bool isNilled, bool &hasStateMachine)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#cvc-complex-type
+
+ // 1
+ if (!isNilled) {
+ XsdComplexType::Ptr complexType;
+
+ if (type->isDefinedBySchema()) {
+ complexType = XsdComplexType::Ptr(type);
+ } else {
+ if (type->name(m_namePool) == BuiltinTypes::xsAnyType->name(m_namePool))
+ complexType = anyType();
+ }
+
+ if (complexType) {
+ // 1.1
+ if (complexType->contentType()->variety() == XsdComplexType::ContentType::Empty) {
+ if (hasChildText() || hasChildElement()) {
+ error(QtXmlPatterns::tr("Element %1 contains not allowed child content.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+
+ // 1.2
+ if (complexType->contentType()->variety() == XsdComplexType::ContentType::Simple) {
+ if (hasChildElement()) {
+ error(QtXmlPatterns::tr("Element %1 contains not allowed child element.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+
+ const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(complexType->contentType()->simpleType(), m_context);
+ QString actualValue;
+ if (hasChildText()) {
+ actualValue = XsdTypeChecker::normalizedValue(text(), facets);
+ } else {
+ if (declaration->valueConstraint())
+ actualValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
+ }
+
+ QString errorMsg;
+ AnySimpleType::Ptr boundType;
+ const XsdTypeChecker checker(m_context, namespaceBindings(item().toNodeModelIndex()), sourceLocation());
+ if (!checker.isValidString(actualValue, complexType->contentType()->simpleType(), errorMsg, &boundType)) {
+ error(QtXmlPatterns::tr("Content of element %1 does not match its type definition: %2.").arg(formatKeyword(declaration->displayName(m_namePool))).arg(errorMsg));
+ return false;
+ }
+
+ // additional check
+ if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
+ if (!checker.valuesAreEqual(actualValue, declaration->valueConstraint()->value(), boundType)) {
+ error(QtXmlPatterns::tr("Content of element %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+ }
+
+ // 1.3
+ if (complexType->contentType()->variety() == XsdComplexType::ContentType::ElementOnly) {
+ if (!text().simplified().isEmpty()) {
+ error(QtXmlPatterns::tr("Element %1 contains not allowed text content.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+
+ // 1.4
+ if (complexType->contentType()->variety() == XsdComplexType::ContentType::ElementOnly ||
+ complexType->contentType()->variety() == XsdComplexType::ContentType::Mixed) {
+
+ if (complexType->contentType()->particle()) {
+ createAndPushStateMachine(complexType->contentType()->particle());
+ hasStateMachine = true;
+ }
+
+ // additional check
+ if (complexType->contentType()->variety() == XsdComplexType::ContentType::Mixed) {
+ if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
+ if (hasChildElement()) {
+ error(QtXmlPatterns::tr("Element %1 cannot contain other elements, as it has a fixed content.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+
+ const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(complexType->contentType()->simpleType(), m_context);
+ QString actualValue;
+ if (hasChildText()) {
+ actualValue = XsdTypeChecker::normalizedValue(text(), facets);
+ } else {
+ if (declaration->valueConstraint())
+ actualValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
+ }
+
+ if (actualValue != declaration->valueConstraint()->value()) {
+ error(QtXmlPatterns::tr("Content of element %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (type->isDefinedBySchema()) {
+ const XsdComplexType::Ptr complexType(type);
+
+ // create a lookup hash for faster access
+ QHash<QXmlName, XsdAttributeUse::Ptr> attributeUseHash;
+ {
+ const XsdAttributeUse::List attributeUses = complexType->attributeUses();
+ for (int i = 0; i < attributeUses.count(); ++i)
+ attributeUseHash.insert(attributeUses.at(i)->attribute()->name(m_namePool), attributeUses.at(i));
+ }
+
+ const QSet<QXmlName> attributes(attributeNames());
+
+ // 3
+ QHashIterator<QXmlName, XsdAttributeUse::Ptr> usesIt(attributeUseHash);
+ while (usesIt.hasNext()) {
+ usesIt.next();
+
+ if (usesIt.value()->isRequired()) {
+ if (!attributes.contains(usesIt.key())) {
+ error(QtXmlPatterns::tr("Element %1 is missing required attribute %2.").arg(formatKeyword(declaration->displayName(m_namePool)))
+ .arg(formatKeyword(m_namePool->displayName(usesIt.key()))));
+ return false;
+ }
+ }
+ }
+
+ bool hasIDAttribute = hasIDAttributeUse(complexType->attributeUses());
+
+ // 2
+ QSetIterator<QXmlName> it(attributes);
+ while (it.hasNext()) {
+ const QXmlName attributeName = it.next();
+
+ // skip builtin attributes
+ if (attributeName == m_xsiNilName ||
+ attributeName == m_xsiTypeName ||
+ attributeName == m_xsiSchemaLocationName ||
+ attributeName == m_xsiNoNamespaceSchemaLocationName)
+ continue;
+
+ // 2.1
+ if (attributeUseHash.contains(attributeName) && (attributeUseHash.value(attributeName)->useType() != XsdAttributeUse::ProhibitedUse)) {
+ if (!validateAttribute(attributeUseHash.value(attributeName), attribute(attributeName)))
+ return false;
+ } else { // 2.2
+ if (complexType->attributeWildcard()) {
+ const XsdWildcard::Ptr wildcard(complexType->attributeWildcard());
+ if (!validateAttributeWildcard(attributeName, wildcard)) {
+ error(QtXmlPatterns::tr("Attribute %1 does not match the attribute wildcard.").arg(formatKeyword(m_namePool->displayName(attributeName))));
+ return false;
+ }
+
+ if (wildcard->processContents() != XsdWildcard::Skip) {
+ const XsdAttribute::Ptr attributeDeclaration = attributeByName(attributeName);
+
+ if (!attributeDeclaration) {
+ if (wildcard->processContents() == XsdWildcard::Strict) {
+ error(QtXmlPatterns::tr("Declaration for attribute %1 does not exist.").arg(formatKeyword(m_namePool->displayName(attributeName))));
+ return false;
+ }
+ } else {
+ if (BuiltinTypes::xsID->wxsTypeMatches(attributeDeclaration->type())) {
+ if (hasIDAttribute) {
+ error(QtXmlPatterns::tr("Element %1 contains two attributes of type %2.")
+ .arg(formatKeyword(declaration->displayName(m_namePool)))
+ .arg(formatKeyword("ID")));
+ return false;
+ }
+
+ hasIDAttribute = true;
+ }
+
+ if (!validateAttribute(attributeDeclaration, attribute(attributeName))) {
+ if (wildcard->processContents() == XsdWildcard::Strict) {
+ error(QtXmlPatterns::tr("Attribute %1 contains invalid content.").arg(formatKeyword(m_namePool->displayName(attributeName))));
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ error(QtXmlPatterns::tr("Element %1 contains unknown attribute %2.").arg(formatKeyword(declaration->displayName(m_namePool)))
+ .arg(formatKeyword(m_namePool->displayName(attributeName))));
+ return false;
+ }
+ }
+ }
+ }
+
+ // 4
+ // so what?...
+
+ // 5
+ // hmm...
+
+ // 6
+ // TODO: check assertions
+
+ // 7
+ // TODO: check type table restrictions
+
+ // rememeber the type of that element node
+ m_model->setAssignedType(item().toNodeModelIndex(), type);
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validateAttribute(const XsdAttributeUse::Ptr &declaration, const QString &value)
+{
+ const AnySimpleType::Ptr attributeType = declaration->attribute()->type();
+ const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(attributeType, m_context);
+
+ const QString actualValue = XsdTypeChecker::normalizedValue(value, facets);
+
+ QString errorMsg;
+ AnySimpleType::Ptr boundType;
+
+ const QXmlNodeModelIndex index = attributeItem(declaration->attribute()->name(m_namePool)).toNodeModelIndex();
+
+ const XsdTypeChecker checker(m_context, namespaceBindings(index), sourceLocation());
+ if (!checker.isValidString(actualValue, attributeType, errorMsg, &boundType)) {
+ error(QtXmlPatterns::tr("Content of attribute %1 does not match its type definition: %2.").arg(formatKeyword(declaration->attribute()->displayName(m_namePool))).arg(errorMsg));
+ return false;
+ }
+
+ // @see http://www.w3.org/TR/xmlschema11-1/#cvc-au
+ if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdAttributeUse::ValueConstraint::Fixed) {
+ const QString actualConstraintValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
+ if (!checker.valuesAreEqual(actualValue, actualConstraintValue, attributeType)) {
+ error(QtXmlPatterns::tr("Content of attribute %1 does not match defined value constraint.").arg(formatKeyword(declaration->attribute()->displayName(m_namePool))));
+ return false;
+ }
+ }
+
+ if (BuiltinTypes::xsID->wxsTypeMatches(declaration->attribute()->type())) {
+ addIdIdRefBinding(actualValue, declaration->attribute());
+ }
+
+ if (m_idRefsType->wxsTypeMatches(declaration->attribute()->type())) {
+ const QStringList idRefs = actualValue.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ for (int i = 0; i < idRefs.count(); ++i)
+ m_idRefs.insert(idRefs.at(i));
+ } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(declaration->attribute()->type())) {
+ m_idRefs.insert(actualValue);
+ }
+
+ m_model->setAssignedType(index, declaration->attribute()->type());
+ m_model->setAssignedAttribute(index, declaration->attribute());
+
+ return true;
+}
+
+//TODO: merge that with the method above
+bool XsdValidatingInstanceReader::validateAttribute(const XsdAttribute::Ptr &declaration, const QString &value)
+{
+ const AnySimpleType::Ptr attributeType = declaration->type();
+ const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(attributeType, m_context);
+
+ const QString actualValue = XsdTypeChecker::normalizedValue(value, facets);
+
+ QString errorMsg;
+ AnySimpleType::Ptr boundType;
+
+ const QXmlNodeModelIndex index = attributeItem(declaration->name(m_namePool)).toNodeModelIndex();
+
+ const XsdTypeChecker checker(m_context, namespaceBindings(index), sourceLocation());
+ if (!checker.isValidString(actualValue, attributeType, errorMsg, &boundType)) {
+ error(QtXmlPatterns::tr("Content of attribute %1 does not match its type definition: %2.").arg(formatKeyword(declaration->displayName(m_namePool))).arg(errorMsg));
+ return false;
+ }
+
+ // @see http://www.w3.org/TR/xmlschema11-1/#cvc-au
+ if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdAttribute::ValueConstraint::Fixed) {
+ const QString actualConstraintValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
+ if (!checker.valuesAreEqual(actualValue, actualConstraintValue, attributeType)) {
+ error(QtXmlPatterns::tr("Content of attribute %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+
+ if (BuiltinTypes::xsID->wxsTypeMatches(declaration->type())) {
+ addIdIdRefBinding(actualValue, declaration);
+ }
+
+ if (m_idRefsType->wxsTypeMatches(declaration->type())) {
+ const QStringList idRefs = actualValue.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ for (int i = 0; i < idRefs.count(); ++i)
+ m_idRefs.insert(idRefs.at(i));
+ } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(declaration->type())) {
+ m_idRefs.insert(actualValue);
+ }
+
+ m_model->setAssignedType(index, declaration->type());
+ m_model->setAssignedAttribute(index, declaration);
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validateAttributeWildcard(const QXmlName &attributeName, const XsdWildcard::Ptr &wildcard)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#cvc-wildcard
+
+ // wildcards using XsdWildcard::absentNamespace, so we have to fix that here
+ QXmlName name(attributeName);
+ if (name.namespaceURI() == StandardNamespaces::empty) {
+ name.setNamespaceURI(m_namePool->allocateNamespace(XsdWildcard::absentNamespace()));
+ }
+
+ return XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard, m_namePool);
+}
+
+bool XsdValidatingInstanceReader::validateIdentityConstraint(const XsdElement::Ptr &element, const QXmlItem &currentItem)
+{
+ const XsdIdentityConstraint::List constraints = element->identityConstraints();
+
+ for (int i = 0; i < constraints.count(); ++i) {
+ const XsdIdentityConstraint::Ptr constraint = constraints.at(i);
+
+ TargetNode::Set targetNodeSet, qualifiedNodeSet;
+ selectNodeSets(element, currentItem, constraint, targetNodeSet, qualifiedNodeSet);
+
+ if (constraint->category() == XsdIdentityConstraint::Unique) {
+ if (!validateUniqueIdentityConstraint(element, constraint, qualifiedNodeSet))
+ return false;
+ } else if (constraint->category() == XsdIdentityConstraint::Key) {
+ if (!validateKeyIdentityConstraint(element, constraint, targetNodeSet, qualifiedNodeSet))
+ return false;
+ }
+ }
+
+ // we do the keyref check in a separated run to make sure that all keys are available
+ for (int i = 0; i < constraints.count(); ++i) {
+ const XsdIdentityConstraint::Ptr constraint = constraints.at(i);
+ if (constraint->category() == XsdIdentityConstraint::KeyReference) {
+ TargetNode::Set targetNodeSet, qualifiedNodeSet;
+ selectNodeSets(element, currentItem, constraint, targetNodeSet, qualifiedNodeSet);
+
+ if (!validateKeyRefIdentityConstraint(element, constraint, qualifiedNodeSet))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validateUniqueIdentityConstraint(const XsdElement::Ptr&, const XsdIdentityConstraint::Ptr &constraint, const TargetNode::Set &qualifiedNodeSet)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#d0e32243
+
+ // 4.1
+ const XsdSchemaSourceLocationReflection reflection(sourceLocation());
+
+ QSetIterator<TargetNode> it(qualifiedNodeSet);
+ while (it.hasNext()) {
+ const TargetNode node = it.next();
+ QSetIterator<TargetNode> innerIt(qualifiedNodeSet);
+ while (innerIt.hasNext()) {
+ const TargetNode innerNode = innerIt.next();
+
+ if (node == innerNode) // do not compare with ourself
+ continue;
+
+ if (node.fieldsAreEqual(innerNode, m_namePool, m_context, &reflection)) {
+ error(QtXmlPatterns::tr("Non-unique value found for constraint %1.").arg(formatKeyword(constraint->displayName(m_namePool))));
+ return false;
+ }
+ }
+ }
+
+ m_idcKeys.insert(constraint->name(m_namePool), qualifiedNodeSet);
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validateKeyIdentityConstraint(const XsdElement::Ptr &element, const XsdIdentityConstraint::Ptr &constraint, const TargetNode::Set &targetNodeSet, const TargetNode::Set &qualifiedNodeSet)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#d0e32243
+
+ // 4.2
+ const XsdSchemaSourceLocationReflection reflection(sourceLocation());
+
+ // 4.2.1
+ if (targetNodeSet.count() != qualifiedNodeSet.count()) {
+ error(QtXmlPatterns::tr("Key constraint %1 contains absent fields.").arg(formatKeyword(constraint->displayName(m_namePool))));
+ return false;
+ }
+
+ // 4.2.2
+ if (!validateUniqueIdentityConstraint(element, constraint, qualifiedNodeSet))
+ return false;
+
+ // 4.2.3
+ QSetIterator<TargetNode> it(qualifiedNodeSet);
+ while (it.hasNext()) {
+ const TargetNode node = it.next();
+ const QVector<QXmlItem> fieldItems = node.fieldItems();
+ for (int i = 0; i < fieldItems.count(); ++i) {
+ const QXmlNodeModelIndex index = fieldItems.at(i).toNodeModelIndex();
+ if (m_model->kind(index) == QXmlNodeModelIndex::Element) {
+ const XsdElement::Ptr declaration = m_model->assignedElement(index);
+ if (declaration && declaration->isNillable()) {
+ error(QtXmlPatterns::tr("Key constraint %1 contains references nillable element %2.")
+ .arg(formatKeyword(constraint->displayName(m_namePool)))
+ .arg(formatKeyword(declaration->displayName(m_namePool))));
+ return false;
+ }
+ }
+ }
+ }
+
+ m_idcKeys.insert(constraint->name(m_namePool), qualifiedNodeSet);
+
+ return true;
+}
+
+bool XsdValidatingInstanceReader::validateKeyRefIdentityConstraint(const XsdElement::Ptr&, const XsdIdentityConstraint::Ptr &constraint, const TargetNode::Set &qualifiedNodeSet)
+{
+ // @see http://www.w3.org/TR/xmlschema11-1/#d0e32243
+
+ // 4.3
+ const XsdSchemaSourceLocationReflection reflection(sourceLocation());
+
+ const TargetNode::Set keySet = m_idcKeys.value(constraint->referencedKey()->name(m_namePool));
+
+ QSetIterator<TargetNode> it(qualifiedNodeSet);
+ while (it.hasNext()) {
+ const TargetNode node = it.next();
+
+ bool foundMatching = false;
+
+ QSetIterator<TargetNode> keyIt(keySet);
+ while (keyIt.hasNext()) {
+ const TargetNode keyNode = keyIt.next();
+
+ if (node.fieldsAreEqual(keyNode, m_namePool, m_context, &reflection)) {
+ foundMatching = true;
+ break;
+ }
+ }
+
+ if (!foundMatching) {
+ error(QtXmlPatterns::tr("No referenced value found for key reference %1.").arg(formatKeyword(constraint->displayName(m_namePool))));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QXmlQuery XsdValidatingInstanceReader::createXQuery(const QList<QXmlName> &namespaceBindings, const QXmlItem &contextNode, const QString &queryString) const
+{
+ // create a public name pool from our name pool
+ QXmlNamePool namePool(m_namePool.data());
+
+ // the QXmlQuery shall work with the same name pool as we do
+ QXmlQuery query(namePool);
+
+ // add additional namespace bindings
+ QXmlQueryPrivate *queryPrivate = query.d;
+
+ for (int i = 0; i < namespaceBindings.count(); ++i) {
+ if (!namespaceBindings.at(i).prefix() == StandardPrefixes::empty)
+ queryPrivate->addAdditionalNamespaceBinding(namespaceBindings.at(i));
+ }
+
+ // set the context node for that query and the query string
+ query.setFocus(contextNode);
+ query.setQuery(queryString, m_documentUri);
+
+ return query;
+}
+
+bool XsdValidatingInstanceReader::selectNodeSets(const XsdElement::Ptr&, const QXmlItem &currentItem, const XsdIdentityConstraint::Ptr &constraint, TargetNode::Set &targetNodeSet, TargetNode::Set &qualifiedNodeSet)
+{
+ // at first select all target nodes
+ const XsdXPathExpression::Ptr selector = constraint->selector();
+ const XsdXPathExpression::List fields = constraint->fields();
+
+ QXmlQuery query = createXQuery(selector->namespaceBindings(), currentItem, selector->expression());
+
+ QXmlResultItems resultItems;
+ query.evaluateTo(&resultItems);
+
+ // now we iterate over all target nodes and select the fields for each node
+ QXmlItem item(resultItems.next());
+ while (!item.isNull()) {
+
+ TargetNode targetNode(item);
+
+ for (int i = 0; i < fields.count(); ++i) {
+ const XsdXPathExpression::Ptr field = fields.at(i);
+ QXmlQuery fieldQuery = createXQuery(field->namespaceBindings(), item, field->expression());
+
+ QXmlResultItems fieldResultItems;
+ fieldQuery.evaluateTo(&fieldResultItems);
+
+ // copy result into vetor for better testing...
+ QVector<QXmlItem> fieldVector;
+ QXmlItem fieldItem(fieldResultItems.next());
+ while (!fieldItem.isNull()) {
+ fieldVector.append(fieldItem);
+ fieldItem = fieldResultItems.next();
+ }
+
+ if (fieldVector.count() > 1) {
+ error(QtXmlPatterns::tr("More than one value found for field %1.").arg(formatData(field->expression())));
+ return false;
+ }
+
+ if (fieldVector.count() == 1) {
+ fieldItem = fieldVector.first();
+
+ const QXmlNodeModelIndex index = fieldItem.toNodeModelIndex();
+ const SchemaType::Ptr type = m_model->assignedType(index);
+
+ bool typeOk = true;
+ if (type->isComplexType()) {
+ if (type->isDefinedBySchema()) {
+ if (XsdComplexType::Ptr(type)->contentType()->variety() != XsdComplexType::ContentType::Simple)
+ typeOk = false;
+ } else {
+ typeOk = false;
+ }
+ }
+ if (!typeOk) {
+ error(QtXmlPatterns::tr("Field %1 has no simple type.").arg(formatData(field->expression())));
+ return false;
+ }
+
+ SchemaType::Ptr targetType = type;
+ QString value = m_model->stringValue(fieldItem.toNodeModelIndex());
+
+ if (type->isDefinedBySchema()) {
+ if (type->isSimpleType())
+ targetType = XsdSimpleType::Ptr(type)->primitiveType();
+ else
+ targetType = XsdComplexType::Ptr(type)->contentType()->simpleType();
+ } else {
+ if (BuiltinTypes::xsAnySimpleType->name(m_namePool) == type->name(m_namePool)) {
+ targetType = BuiltinTypes::xsString;
+ value = QLatin1String("___anySimpleType_value");
+ }
+ }
+
+ // if it is xs:QName derived type, we normalize the name content
+ // and do a string comparison
+ if (BuiltinTypes::xsQName->wxsTypeMatches(type)) {
+ targetType = BuiltinTypes::xsString;
+
+ const QXmlName qName = convertToQName(value.trimmed());
+ value = QString::fromLatin1("%1:%2").arg(m_namePool->stringForNamespace(qName.namespaceURI())).arg(m_namePool->stringForLocalName(qName.localName()));
+ }
+
+ targetNode.addField(fieldItem, value, targetType);
+ } else {
+ // we add an empty entry here, that makes comparison easier later on
+ targetNode.addField(QXmlItem(), QString(), SchemaType::Ptr());
+ }
+ }
+
+ targetNodeSet.insert(targetNode);
+
+ item = resultItems.next();
+ }
+
+ // copy all items from target node set to qualified node set, that have no empty fields
+ QSetIterator<TargetNode> it(targetNodeSet);
+ while (it.hasNext()) {
+ const TargetNode node = it.next();
+ if (node.emptyFieldsCount() == 0)
+ qualifiedNodeSet.insert(node);
+ }
+
+ return true;
+}
+
+XsdElement::Ptr XsdValidatingInstanceReader::elementByName(const QXmlName &name) const
+{
+ return m_schema->element(name);
+}
+
+XsdAttribute::Ptr XsdValidatingInstanceReader::attributeByName(const QXmlName &name) const
+{
+ return m_schema->attribute(name);
+}
+
+SchemaType::Ptr XsdValidatingInstanceReader::typeByName(const QXmlName &name) const
+{
+ const SchemaType::Ptr type = m_schema->type(name);
+ if (type)
+ return type;
+
+ return m_context->schemaTypeFactory()->createSchemaType(name);
+}
+
+void XsdValidatingInstanceReader::addIdIdRefBinding(const QString &id, const NamedSchemaComponent::Ptr &binding)
+{
+ if (!m_model->idIdRefBindings(id).isEmpty()) {
+ error(QtXmlPatterns::tr("ID value '%1' is not unique.").arg(formatKeyword(id)));
+ return;
+ }
+
+ m_model->addIdIdRefBinding(id, binding);
+}
+
+QString XsdValidatingInstanceReader::qNameAttribute(const QXmlName &attributeName)
+{
+ const QString value = attribute(attributeName).simplified();
+ if (!XPathHelper::isQName(value)) {
+ error(QtXmlPatterns::tr("'%1' attribute contains invalid QName content: %2.").arg(m_namePool->displayName(attributeName)).arg(formatData(value)));
+ return QString();
+ } else {
+ return value;
+ }
+}
+
+XsdComplexType::Ptr XsdValidatingInstanceReader::anyType()
+{
+ if (m_anyType)
+ return m_anyType;
+
+ const XsdWildcard::Ptr wildcard(new XsdWildcard());
+ wildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
+ wildcard->setProcessContents(XsdWildcard::Lax);
+
+ const XsdParticle::Ptr outerParticle(new XsdParticle());
+ outerParticle->setMinimumOccurs(1);
+ outerParticle->setMaximumOccurs(1);
+
+ const XsdParticle::Ptr innerParticle(new XsdParticle());
+ innerParticle->setMinimumOccurs(0);
+ innerParticle->setMaximumOccursUnbounded(true);
+ innerParticle->setTerm(wildcard);
+
+ const XsdModelGroup::Ptr outerModelGroup(new XsdModelGroup());
+ outerModelGroup->setCompositor(XsdModelGroup::SequenceCompositor);
+ outerModelGroup->setParticles(XsdParticle::List() << innerParticle);
+ outerParticle->setTerm(outerModelGroup);
+
+ m_anyType = XsdComplexType::Ptr(new XsdComplexType());
+ m_anyType->setName(BuiltinTypes::xsAnyType->name(m_namePool));
+ m_anyType->setDerivationMethod(XsdComplexType::DerivationRestriction);
+ m_anyType->contentType()->setVariety(XsdComplexType::ContentType::Mixed);
+ m_anyType->contentType()->setParticle(outerParticle);
+ m_anyType->setAttributeWildcard(wildcard);
+ m_anyType->setIsAbstract(false);
+
+ return m_anyType;
+}
+
+QT_END_NAMESPACE