1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qxsdvalidatinginstancereader_p.h"
41 
42 #include "qabstractdatetime_p.h"
43 #include "qacceltreeresourceloader_p.h"
44 #include "qbase64binary_p.h"
45 #include "qboolean_p.h"
46 #include "qcommonnamespaces_p.h"
47 #include "qderivedinteger_p.h"
48 #include "qduration_p.h"
49 #include "qgenericstaticcontext_p.h"
50 #include "qhexbinary_p.h"
51 #include "qnamespaceresolver_p.h"
52 #include "qpatternplatform_p.h"
53 #include "qqnamevalue_p.h"
54 #include "qsourcelocationreflection_p.h"
55 #include "qvaluefactory_p.h"
56 #include "qxmlnamepool.h"
57 #include "qxmlquery_p.h"
58 #include "qxmlschema_p.h"
59 #include "qxsdschemahelper_p.h"
60 #include "qxsdschemamerger_p.h"
61 #include "qxsdstatemachine_p.h"
62 #include "qxsdstatemachinebuilder_p.h"
63 #include "qxsdtypechecker_p.h"
64 
65 #include "qxsdschemadebugger_p.h"
66 
67 #include <QtCore/QFile>
68 #include <QtXmlPatterns/QXmlQuery>
69 #include <QtXmlPatterns/QXmlResultItems>
70 
71 QT_BEGIN_NAMESPACE
72 
73 using namespace QPatternist;
74 
75 namespace QPatternist
76 {
77     template <>
78     template <>
inputEqualsTransition(QXmlName name,XsdTerm::Ptr term) const79     bool XsdStateMachine<XsdTerm::Ptr>::inputEqualsTransition<QXmlName>(QXmlName name, XsdTerm::Ptr term) const
80     {
81         if (term->isElement()) {
82             return (XsdElement::Ptr(term)->name(m_namePool) == name);
83         } else if (term->isWildcard()) {
84             // wildcards using XsdWildcard::absentNamespace, so we have to fix that here
85             if (name.namespaceURI() == StandardNamespaces::empty) {
86                 name.setNamespaceURI(m_namePool->allocateNamespace(XsdWildcard::absentNamespace()));
87             }
88 
89             return XsdSchemaHelper::wildcardAllowsExpandedName(name, XsdWildcard::Ptr(term), m_namePool);
90         }
91 
92         return false;
93     }
94 }
95 
XsdValidatingInstanceReader(XsdValidatedXmlNodeModel * model,const QUrl & documentUri,const XsdSchemaContext::Ptr & context)96 XsdValidatingInstanceReader::XsdValidatingInstanceReader(XsdValidatedXmlNodeModel *model, const QUrl &documentUri, const XsdSchemaContext::Ptr &context)
97     : XsdInstanceReader(model, context)
98     , m_model(model)
99     , m_namePool(m_context->namePool())
100     , m_xsiNilName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("nil")))
101     , m_xsiTypeName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("type")))
102     , m_xsiSchemaLocationName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("schemaLocation")))
103     , m_xsiNoNamespaceSchemaLocationName(m_namePool->allocateQName(CommonNamespaces::XSI, QLatin1String("noNamespaceSchemaLocation")))
104     , m_documentUri(documentUri)
105 {
106     m_idRefsType = m_context->schemaTypeFactory()->createSchemaType(m_namePool->allocateQName(CommonNamespaces::WXS, QLatin1String("IDREFS")));
107 }
108 
addSchema(const XsdSchema::Ptr & schema,const QUrl & locationUrl)109 void XsdValidatingInstanceReader::addSchema(const XsdSchema::Ptr &schema, const QUrl &locationUrl)
110 {
111     if (!m_mergedSchemas.contains(locationUrl)) {
112         m_mergedSchemas.insert(locationUrl, QStringList() << schema->targetNamespace());
113     } else {
114         QStringList &targetNamespaces = m_mergedSchemas[locationUrl];
115         if (targetNamespaces.contains(schema->targetNamespace()))
116             return;
117 
118         targetNamespaces.append(schema->targetNamespace());
119     }
120 
121     const XsdSchemaMerger merger(m_schema, schema);
122     m_schema = merger.mergedSchema();
123 /*
124     XsdSchemaDebugger dbg(m_namePool);
125     dbg.dumpSchema(m_schema);
126 */
127 }
128 
read()129 bool XsdValidatingInstanceReader::read()
130 {
131     while (!atEnd()) {
132         readNext();
133 
134         if (isEndElement())
135             return true;
136 
137         if (isStartElement()) {
138             const QXmlName elementName = name();
139             const QXmlItem currentItem = item();
140             bool hasStateMachine = false;
141             XsdElement::Ptr processedElement;
142 
143             if (!validate(hasStateMachine, processedElement))
144                 return false;
145 
146             read();
147 
148             if (processedElement) { // for wildcard with 'skip' we have no element
149                 m_model->setAssignedElement(currentItem.toNodeModelIndex(), processedElement);
150 
151                 // check identity constraints after all child nodes have been
152                 // validated, so that we know there assigned types
153                 validateIdentityConstraint(processedElement, currentItem);
154             }
155 
156             if (!m_stateMachines.isEmpty() && hasStateMachine) {
157                 if (!m_stateMachines.top().inEndState()) {
158                     error(QtXmlPatterns::tr("Element %1 is missing child element.").arg(formatKeyword(m_namePool->displayName(elementName))));
159                     return false;
160                 }
161                 m_stateMachines.pop();
162             }
163         }
164     }
165 
166     // final validations
167 
168     // check IDREF occurrences
169     const QStringList ids = m_model->idIdRefBindingIds();
170     for (const QString &id : qAsConst(m_idRefs)) {
171         if (!ids.contains(id)) {
172             error(QtXmlPatterns::tr("There is one IDREF value with no corresponding ID: %1.").arg(formatKeyword(id)));
173             return false;
174         }
175     }
176 
177     return true;
178 }
179 
error(const QString & msg) const180 void XsdValidatingInstanceReader::error(const QString &msg) const
181 {
182     m_context.data()->error(msg, XsdSchemaContext::XSDError, sourceLocation());
183 }
184 
loadSchema(const QString & targetNamespace,const QUrl & location)185 bool XsdValidatingInstanceReader::loadSchema(const QString &targetNamespace, const QUrl &location)
186 {
187     const AutoPtr<QNetworkReply> reply(AccelTreeResourceLoader::load(location, m_context->networkAccessManager(),
188                                                                      m_context, AccelTreeResourceLoader::ContinueOnError));
189     if (!reply)
190         return true;
191 
192     // we have to create a separated schema context here, that however shares the type factory
193     XsdSchemaContext::Ptr context(new XsdSchemaContext(m_namePool));
194     context->m_schemaTypeFactory = m_context->m_schemaTypeFactory;
195 
196     QXmlSchemaPrivate schema(context);
197     schema.load(reply.data(), location, targetNamespace);
198     if (!schema.isValid()) {
199         error(QtXmlPatterns::tr("Loaded schema file is invalid."));
200         return false;
201     }
202 
203     addSchema(schema.m_schemaParserContext->schema(), location);
204 
205     return true;
206 }
207 
validate(bool & hasStateMachine,XsdElement::Ptr & processedElement)208 bool XsdValidatingInstanceReader::validate(bool &hasStateMachine, XsdElement::Ptr &processedElement)
209 {
210     // first check if a custom schema is defined
211     if (hasAttribute(m_xsiSchemaLocationName)) {
212         const QString schemaLocation = attribute(m_xsiSchemaLocationName);
213         const QStringList parts = schemaLocation.split(QLatin1Char(' '), Qt::SkipEmptyParts);
214         if ((parts.count()%2) == 1) {
215             error(QtXmlPatterns::tr("%1 contains invalid data.").arg(formatKeyword(m_namePool, m_xsiSchemaLocationName)));
216             return false;
217         }
218 
219         for (int i = 0; i < parts.count(); i += 2) {
220             const QString identifier = QString::fromLatin1("%1 %2").arg(parts.at(i)).arg(parts.at(i + 1));
221             if (m_processedSchemaLocations.contains(identifier))
222                 continue;
223             else
224                 m_processedSchemaLocations.insert(identifier);
225 
226             // check constraint 4) from http://www.w3.org/TR/xmlschema-1/#schema-loc (only valid for XML Schema 1.0?)
227             if (m_processedNamespaces.contains(parts.at(i))) {
228                 error(QtXmlPatterns::tr("xsi:schemaLocation namespace %1 has already appeared earlier in the instance document.").arg(formatKeyword(parts.at(i))));
229                 return false;
230             }
231 
232             QUrl url(parts.at(i + 1));
233             if (url.isRelative()) {
234                 Q_ASSERT(m_documentUri.isValid());
235 
236                 url = m_documentUri.resolved(url);
237             }
238 
239             loadSchema(parts.at(i), url);
240         }
241     }
242 
243     if (hasAttribute(m_xsiNoNamespaceSchemaLocationName)) {
244         const QString schemaLocation = attribute(m_xsiNoNamespaceSchemaLocationName);
245 
246         if (!m_processedSchemaLocations.contains(schemaLocation)) {
247             m_processedSchemaLocations.insert(schemaLocation);
248 
249             if (m_processedNamespaces.contains(QString())) {
250                 error(QtXmlPatterns::tr("xsi:noNamespaceSchemaLocation cannot appear after the first no-namespace element or attribute."));
251                 return false;
252             }
253 
254             QUrl url(schemaLocation);
255             if (url.isRelative()) {
256                 Q_ASSERT(m_documentUri.isValid());
257 
258                 url = m_documentUri.resolved(url);
259             }
260 
261             loadSchema(QString(), url);
262         }
263     }
264 
265     m_processedNamespaces.insert(m_namePool->stringForNamespace(name().namespaceURI()));
266 
267     if (!m_schema) {
268         error(QtXmlPatterns::tr("No schema defined for validation."));
269         return false;
270     }
271 
272     // check if we are 'inside' a type definition
273     if (m_stateMachines.isEmpty()) {
274         // find out the type of the top-level element
275         XsdElement::Ptr element = elementByName(name());
276         if (!element) {
277             if (!hasAttribute(m_xsiTypeName)) {
278                 error(QtXmlPatterns::tr("No definition for element %1 available.").arg(formatKeyword(m_namePool, name())));
279                 return false;
280             }
281 
282             // This instance document has an element with no definition in the schema
283             // but an explicitly given type, that is fine according to the spec.
284             // We will create an element definition manually here and continue the
285             // normal validation process
286             element = XsdElement::Ptr(new XsdElement());
287             element->setName(name());
288             element->setIsAbstract(false);
289             element->setIsNillable(hasAttribute(m_xsiNilName));
290 
291             const QString type = qNameAttribute(m_xsiTypeName);
292             const QXmlName typeName = convertToQName(type);
293 
294             const SchemaType::Ptr elementType = typeByName(typeName);
295             if (!elementType) {
296                 error(QtXmlPatterns::tr("Specified type %1 is not known to the schema.").arg(formatType(m_namePool, typeName)));
297                 return false;
298             }
299             element->setType(elementType);
300         }
301 
302         // rememeber the element we process
303         processedElement = element;
304 
305         if (!validateElement(element, hasStateMachine)) {
306             return false;
307         }
308 
309     } else {
310         if (!m_stateMachines.top().proceed<QXmlName>(name())) {
311             error(QtXmlPatterns::tr("Element %1 is not defined in this scope.").arg(formatKeyword(m_namePool, name())));
312             return false;
313         }
314 
315         const XsdTerm::Ptr term = m_stateMachines.top().lastTransition();
316         if (term->isElement()) {
317             const XsdElement::Ptr element(term);
318 
319             // rememeber the element we process
320             processedElement = element;
321 
322             if (!validateElement(element, hasStateMachine))
323                 return false;
324 
325         } else {
326             const XsdWildcard::Ptr wildcard(term);
327             if (wildcard->processContents() != XsdWildcard::Skip) {
328                 XsdElement::Ptr elementDeclaration = elementByName(name());
329                 if (!elementDeclaration) {
330                     if (hasAttribute(m_xsiTypeName)) {
331                         // This instance document has an element with no definition in the schema
332                         // but an explicitly given type, that is fine according to the spec.
333                         // We will create an element definition manually here and continue the
334                         // normal validation process
335                         elementDeclaration = XsdElement::Ptr(new XsdElement());
336                         elementDeclaration->setName(name());
337                         elementDeclaration->setIsAbstract(false);
338                         elementDeclaration->setIsNillable(hasAttribute(m_xsiNilName));
339 
340                         const QString type = qNameAttribute(m_xsiTypeName);
341                         const QXmlName typeName = convertToQName(type);
342 
343                         const SchemaType::Ptr elementType = typeByName(typeName);
344                         if (!elementType) {
345                             error(QtXmlPatterns::tr("Specified type %1 is not known to the schema.").arg(formatType(m_namePool, typeName)));
346                             return false;
347                         }
348                         elementDeclaration->setType(elementType);
349                     }
350                 }
351 
352                 if (!elementDeclaration) {
353                     if (wildcard->processContents() == XsdWildcard::Strict) {
354                         error(QtXmlPatterns::tr("Declaration for element %1 does not exist.").arg(formatKeyword(m_namePool->displayName(name()))));
355                         return false;
356                     } else {
357                         // in this case we put a state machine for the xs:anyType on the statemachine stack,
358                         // so we accept every content of this element
359 
360                         createAndPushStateMachine(anyType()->contentType()->particle());
361                         hasStateMachine = true;
362                     }
363                 } else {
364                     if (!validateElement(elementDeclaration, hasStateMachine)) {
365                         if (wildcard->processContents() == XsdWildcard::Strict) {
366                             error(QtXmlPatterns::tr("Element %1 contains invalid content.").arg(formatKeyword(m_namePool->displayName(name()))));
367                             return false;
368                         }
369                     }
370 
371                     // rememeber the type of that element node
372                     m_model->setAssignedType(item().toNodeModelIndex(), elementDeclaration->type());
373                 }
374             } else { // wildcard process contents type is Skip
375                 // in this case we put a state machine for the xs:anyType on the statemachine stack,
376                 // so we accept every content of this element
377 
378                 const XsdWildcard::Ptr wildcard(new XsdWildcard());
379                 wildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
380                 wildcard->setProcessContents(XsdWildcard::Skip);
381 
382                 const XsdParticle::Ptr outerParticle(new XsdParticle());
383                 outerParticle->setMinimumOccurs(1);
384                 outerParticle->setMaximumOccurs(1);
385 
386                 const XsdParticle::Ptr innerParticle(new XsdParticle());
387                 innerParticle->setMinimumOccurs(0);
388                 innerParticle->setMaximumOccursUnbounded(true);
389                 innerParticle->setTerm(wildcard);
390 
391                 const XsdModelGroup::Ptr outerModelGroup(new XsdModelGroup());
392                 outerModelGroup->setCompositor(XsdModelGroup::SequenceCompositor);
393                 outerModelGroup->setParticles(XsdParticle::List() << innerParticle);
394                 outerParticle->setTerm(outerModelGroup);
395 
396                 createAndPushStateMachine(outerParticle);
397                 hasStateMachine = true;
398             }
399         }
400     }
401 
402     return true;
403 }
404 
createAndPushStateMachine(const XsdParticle::Ptr & particle)405 void XsdValidatingInstanceReader::createAndPushStateMachine(const XsdParticle::Ptr &particle)
406 {
407     XsdStateMachine<XsdTerm::Ptr> stateMachine(m_namePool);
408 
409     XsdStateMachineBuilder builder(&stateMachine, m_namePool, XsdStateMachineBuilder::ValidatingMode);
410     const XsdStateMachine<XsdTerm::Ptr>::StateId endState = builder.reset();
411     const XsdStateMachine<XsdTerm::Ptr>::StateId startState = builder.buildParticle(particle, endState);
412     builder.addStartState(startState);
413 
414 /*
415     QString fileName = QString("/tmp/foo_%1.dot").arg(m_namePool->displayName(complexType->name(m_namePool)));
416     QString pngFileName = QString("/tmp/foo_%1.png").arg(m_namePool->displayName(complexType->name(m_namePool)));
417     QFile file(fileName);
418     file.open(QIODevice::WriteOnly);
419     stateMachine.outputGraph(&file, "Hello");
420     file.close();
421     ::system(QString("dot -Tpng %1 -o%2").arg(fileName).arg(pngFileName).toLatin1().data());
422 */
423 
424     stateMachine = stateMachine.toDFA();
425 
426     m_stateMachines.push(stateMachine);
427 }
428 
validateElement(const XsdElement::Ptr & declaration,bool & hasStateMachine)429 bool XsdValidatingInstanceReader::validateElement(const XsdElement::Ptr &declaration, bool &hasStateMachine)
430 {
431     // http://www.w3.org/TR/xmlschema11-1/#d0e10998
432 
433     bool isNilled = false;
434 
435     // 1 tested already, 'declaration' corresponds D
436 
437     // 2
438     if (declaration->isAbstract()) {
439         error(QtXmlPatterns::tr("Element %1 is declared as abstract.").arg(formatKeyword(declaration->displayName(m_namePool))));
440         return false;
441     }
442 
443     // 3
444     if (!declaration->isNillable()) {
445         if (hasAttribute(m_xsiNilName)) {
446             error(QtXmlPatterns::tr("Element %1 is not nillable.").arg(formatKeyword(declaration->displayName(m_namePool))));
447             return false; // 3.1
448         }
449     } else {
450         if (hasAttribute(m_xsiNilName)) {
451             const QString value = attribute(m_xsiNilName);
452             const Boolean::Ptr nil = Boolean::fromLexical(value);
453             if (nil->hasError()) {
454                 error(QtXmlPatterns::tr("Attribute %1 contains invalid data: %2").arg(formatKeyword(QLatin1String("nil."))).arg(formatData(value)));
455                 return false;
456             }
457 
458             // 3.2.3
459             if (nil->as<Boolean>()->value() == true) {
460                 // 3.2.3.1
461                 if (hasChildElement() || hasChildText()) {
462                     error(QtXmlPatterns::tr("Element contains content although it is nillable."));
463                     return false;
464                 }
465 
466                 // 3.2.3.2
467                 if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
468                     error(QtXmlPatterns::tr("Fixed value constraint not allowed if element is nillable."));
469                     return false;
470                 }
471             }
472 
473             isNilled = nil->as<Boolean>()->value();
474         }
475     }
476 
477     SchemaType::Ptr finalElementType = declaration->type();
478 
479     // 4
480     if (hasAttribute(m_xsiTypeName)) {
481         const QString type = qNameAttribute(m_xsiTypeName);
482         const QXmlName typeName = convertToQName(type);
483 
484         const SchemaType::Ptr elementType = typeByName(typeName);
485         // 4.1
486         if (!elementType) {
487             error(QtXmlPatterns::tr("Specified type %1 is not known to the schema.").arg(formatType(m_namePool, typeName)));
488             return false;
489         }
490 
491         // 4.2
492         SchemaType::DerivationConstraints constraints;
493         if (declaration->disallowedSubstitutions() & NamedSchemaComponent::ExtensionConstraint)
494             constraints |= SchemaType::ExtensionConstraint;
495         if (declaration->disallowedSubstitutions() & NamedSchemaComponent::RestrictionConstraint)
496             constraints |= SchemaType::RestrictionConstraint;
497 
498         if (!XsdSchemaHelper::isValidlySubstitutable(elementType, declaration->type(), constraints)) {
499             if (declaration->type()->name(m_namePool) != BuiltinTypes::xsAnyType->name(m_namePool)) { // xs:anyType is a valid substitutable type here
500                 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())));
501                 return false;
502             }
503         }
504 
505         finalElementType = elementType;
506     }
507 
508     if (!validateElementType(declaration, finalElementType, isNilled, hasStateMachine))
509         return false;
510 
511     return true;
512 }
513 
validateElementType(const XsdElement::Ptr & declaration,const SchemaType::Ptr & type,bool isNilled,bool & hasStateMachine)514 bool XsdValidatingInstanceReader::validateElementType(const XsdElement::Ptr &declaration, const SchemaType::Ptr &type, bool isNilled, bool &hasStateMachine)
515 {
516     // @see http://www.w3.org/TR/xmlschema11-1/#d0e11749
517 
518     // 1 checked already
519 
520     // 2
521     if (type->isComplexType() && type->isDefinedBySchema()) {
522         if (XsdComplexType::Ptr(type)->isAbstract()) {
523             error(QtXmlPatterns::tr("Complex type %1 is not allowed to be abstract.").arg(formatType(m_namePool, type)));
524             return false;
525         }
526     }
527 
528     // 3
529     if (type->isSimpleType())
530         return validateElementSimpleType(declaration, type, isNilled); // 3.1
531     else
532         return validateElementComplexType(declaration, type, isNilled, hasStateMachine); // 3.2
533 }
534 
validateElementSimpleType(const XsdElement::Ptr & declaration,const SchemaType::Ptr & type,bool isNilled)535 bool XsdValidatingInstanceReader::validateElementSimpleType(const XsdElement::Ptr &declaration, const SchemaType::Ptr &type, bool isNilled)
536 {
537     // @see http://www.w3.org/TR/xmlschema11-1/#d0e11749
538 
539     // 3.1.1
540     const QSet<QXmlName> allowedAttributes(QSet<QXmlName>() << m_xsiNilName << m_xsiTypeName << m_xsiSchemaLocationName << m_xsiNoNamespaceSchemaLocationName);
541     QSet<QXmlName> elementAttributes = attributeNames();
542     elementAttributes.subtract(allowedAttributes);
543     if (!elementAttributes.isEmpty()) {
544         error(QtXmlPatterns::tr("Element %1 contains not allowed attributes.").arg(formatKeyword(declaration->displayName(m_namePool))));
545         return false;
546     }
547 
548     // 3.1.2
549     if (hasChildElement()) {
550         error(QtXmlPatterns::tr("Element %1 contains not allowed child element.").arg(formatKeyword(declaration->displayName(m_namePool))));
551         return false;
552     }
553 
554     // 3.1.3
555     if (!isNilled) {
556         const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type, m_context);
557 
558         QString actualValue;
559         if (hasChildText()) {
560             actualValue = XsdTypeChecker::normalizedValue(text(), facets);
561         } else {
562             if (declaration->valueConstraint())
563                 actualValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
564         }
565 
566         QString errorMsg;
567         AnySimpleType::Ptr boundType;
568 
569         const XsdTypeChecker checker(m_context, namespaceBindings(item().toNodeModelIndex()), sourceLocation());
570         if (!checker.isValidString(actualValue, type, errorMsg, &boundType)) {
571             error(QtXmlPatterns::tr("Content of element %1 does not match its type definition: %2.").arg(formatKeyword(declaration->displayName(m_namePool))).arg(errorMsg));
572             return false;
573         }
574 
575         // additional check
576         if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
577             const QString actualConstraintValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
578             if (!text().isEmpty() && !checker.valuesAreEqual(actualValue, actualConstraintValue, type)) {
579                 error(QtXmlPatterns::tr("Content of element %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
580                 return false;
581             }
582         }
583     }
584 
585     // 4  checked in validateElement already
586 
587     // rememeber the type of that element node
588     m_model->setAssignedType(item().toNodeModelIndex(), type);
589 
590     const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type, m_context);
591     const QString actualValue = XsdTypeChecker::normalizedValue(text(), facets);
592 
593     if (BuiltinTypes::xsID->wxsTypeMatches(type)) {
594         addIdIdRefBinding(actualValue, declaration);
595     }
596 
597     if (m_idRefsType->wxsTypeMatches(type)) {
598         const QStringList idRefs = actualValue.split(QLatin1Char(' '), Qt::SkipEmptyParts);
599         for (int i = 0; i < idRefs.count(); ++i) {
600             m_idRefs.insert(idRefs.at(i));
601         }
602     } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(type)) {
603         m_idRefs.insert(actualValue);
604     }
605 
606     return true;
607 }
608 
hasIDAttributeUse(const XsdAttributeUse::List & uses)609 static bool hasIDAttributeUse(const XsdAttributeUse::List &uses)
610 {
611     const int count = uses.count();
612     for (int i = 0; i < count; ++i) {
613         if (BuiltinTypes::xsID->wxsTypeMatches(uses.at(i)->attribute()->type()))
614             return true;
615     }
616 
617     return false;
618 }
619 
validateElementComplexType(const XsdElement::Ptr & declaration,const SchemaType::Ptr & type,bool isNilled,bool & hasStateMachine)620 bool XsdValidatingInstanceReader::validateElementComplexType(const XsdElement::Ptr &declaration, const SchemaType::Ptr &type, bool isNilled, bool &hasStateMachine)
621 {
622     // @see http://www.w3.org/TR/xmlschema11-1/#cvc-complex-type
623 
624     // 1
625     if (!isNilled) {
626         XsdComplexType::Ptr complexType;
627 
628         if (type->isDefinedBySchema()) {
629             complexType = XsdComplexType::Ptr(type);
630         } else {
631             if (type->name(m_namePool) == BuiltinTypes::xsAnyType->name(m_namePool))
632                 complexType = anyType();
633         }
634 
635         if (complexType) {
636             // 1.1
637             if (complexType->contentType()->variety() == XsdComplexType::ContentType::Empty) {
638                 if (hasChildText() || hasChildElement()) {
639                     error(QtXmlPatterns::tr("Element %1 contains not allowed child content.").arg(formatKeyword(declaration->displayName(m_namePool))));
640                     return false;
641                 }
642             }
643 
644             // 1.2
645             if (complexType->contentType()->variety() == XsdComplexType::ContentType::Simple) {
646                 if (hasChildElement()) {
647                     error(QtXmlPatterns::tr("Element %1 contains not allowed child element.").arg(formatKeyword(declaration->displayName(m_namePool))));
648                     return false;
649                 }
650 
651                 const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(complexType->contentType()->simpleType(), m_context);
652                 QString actualValue;
653                 if (hasChildText()) {
654                     actualValue = XsdTypeChecker::normalizedValue(text(), facets);
655                 } else {
656                     if (declaration->valueConstraint())
657                         actualValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
658                 }
659 
660                 QString errorMsg;
661                 AnySimpleType::Ptr boundType;
662                 const XsdTypeChecker checker(m_context, namespaceBindings(item().toNodeModelIndex()), sourceLocation());
663                 if (!checker.isValidString(actualValue, complexType->contentType()->simpleType(), errorMsg, &boundType)) {
664                     error(QtXmlPatterns::tr("Content of element %1 does not match its type definition: %2.").arg(formatKeyword(declaration->displayName(m_namePool))).arg(errorMsg));
665                     return false;
666                 }
667 
668                 // additional check
669                 if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
670                     if (!checker.valuesAreEqual(actualValue, declaration->valueConstraint()->value(), boundType)) {
671                         error(QtXmlPatterns::tr("Content of element %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
672                         return false;
673                     }
674                 }
675             }
676 
677             // 1.3
678             if (complexType->contentType()->variety() == XsdComplexType::ContentType::ElementOnly) {
679                 if (!text().simplified().isEmpty()) {
680                     error(QtXmlPatterns::tr("Element %1 contains not allowed text content.").arg(formatKeyword(declaration->displayName(m_namePool))));
681                     return false;
682                 }
683             }
684 
685             // 1.4
686             if (complexType->contentType()->variety() == XsdComplexType::ContentType::ElementOnly ||
687                 complexType->contentType()->variety() == XsdComplexType::ContentType::Mixed) {
688 
689                 if (complexType->contentType()->particle()) {
690                     createAndPushStateMachine(complexType->contentType()->particle());
691                     hasStateMachine = true;
692                 }
693 
694                 // additional check
695                 if (complexType->contentType()->variety() == XsdComplexType::ContentType::Mixed) {
696                     if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
697                         if (hasChildElement()) {
698                             error(QtXmlPatterns::tr("Element %1 cannot contain other elements, as it has a fixed content.").arg(formatKeyword(declaration->displayName(m_namePool))));
699                             return false;
700                         }
701 
702                         const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(complexType->contentType()->simpleType(), m_context);
703                         QString actualValue;
704                         if (hasChildText()) {
705                             actualValue = XsdTypeChecker::normalizedValue(text(), facets);
706                         } else {
707                             if (declaration->valueConstraint())
708                                 actualValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
709                         }
710 
711                         if (actualValue != declaration->valueConstraint()->value()) {
712                             error(QtXmlPatterns::tr("Content of element %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
713                             return false;
714                         }
715                     }
716                 }
717             }
718         }
719     }
720 
721     if (type->isDefinedBySchema()) {
722         const XsdComplexType::Ptr complexType(type);
723 
724         // create a lookup hash for faster access
725         QHash<QXmlName, XsdAttributeUse::Ptr> attributeUseHash;
726         {
727             const XsdAttributeUse::List attributeUses = complexType->attributeUses();
728             for (int i = 0; i < attributeUses.count(); ++i)
729                 attributeUseHash.insert(attributeUses.at(i)->attribute()->name(m_namePool), attributeUses.at(i));
730         }
731 
732         const QSet<QXmlName> attributes(attributeNames());
733 
734         // 3
735         for (auto it = attributeUseHash.cbegin(), end = attributeUseHash.cend(); it != end; ++it) {
736             if (it.value()->isRequired()) {
737                 if (!attributes.contains(it.key())) {
738                     error(QtXmlPatterns::tr("Element %1 is missing required attribute %2.").arg(formatKeyword(declaration->displayName(m_namePool)))
739                                                                                           .arg(formatKeyword(m_namePool->displayName(it.key()))));
740                     return false;
741                 }
742             }
743         }
744 
745         bool hasIDAttribute = hasIDAttributeUse(complexType->attributeUses());
746 
747         // 2
748         for (const QXmlName &attributeName : attributes) {
749 
750             // skip builtin attributes
751             if (attributeName == m_xsiNilName ||
752                 attributeName == m_xsiTypeName ||
753                 attributeName == m_xsiSchemaLocationName ||
754                 attributeName == m_xsiNoNamespaceSchemaLocationName)
755                 continue;
756 
757             // 2.1
758             if (attributeUseHash.contains(attributeName) && (attributeUseHash.value(attributeName)->useType() != XsdAttributeUse::ProhibitedUse)) {
759                 if (!validateAttribute(attributeUseHash.value(attributeName), attribute(attributeName)))
760                     return false;
761             } else { // 2.2
762                 if (complexType->attributeWildcard()) {
763                     const XsdWildcard::Ptr wildcard(complexType->attributeWildcard());
764                     if (!validateAttributeWildcard(attributeName, wildcard)) {
765                         error(QtXmlPatterns::tr("Attribute %1 does not match the attribute wildcard.").arg(formatKeyword(m_namePool->displayName(attributeName))));
766                         return false;
767                     }
768 
769                     if (wildcard->processContents() != XsdWildcard::Skip) {
770                         const XsdAttribute::Ptr attributeDeclaration = attributeByName(attributeName);
771 
772                         if (!attributeDeclaration) {
773                             if (wildcard->processContents() == XsdWildcard::Strict) {
774                                 error(QtXmlPatterns::tr("Declaration for attribute %1 does not exist.").arg(formatKeyword(m_namePool->displayName(attributeName))));
775                                 return false;
776                             }
777                         } else {
778                             if (BuiltinTypes::xsID->wxsTypeMatches(attributeDeclaration->type())) {
779                                 if (hasIDAttribute) {
780                                     error(QtXmlPatterns::tr("Element %1 contains two attributes of type %2.")
781                                                            .arg(formatKeyword(declaration->displayName(m_namePool)))
782                                                            .arg(formatKeyword("ID")));
783                                     return false;
784                                 }
785 
786                                 hasIDAttribute = true;
787                             }
788 
789                             if (!validateAttribute(attributeDeclaration, attribute(attributeName))) {
790                                 if (wildcard->processContents() == XsdWildcard::Strict) {
791                                     error(QtXmlPatterns::tr("Attribute %1 contains invalid content.").arg(formatKeyword(m_namePool->displayName(attributeName))));
792                                     return false;
793                                 }
794                             }
795                         }
796                     }
797                 } else {
798                     error(QtXmlPatterns::tr("Element %1 contains unknown attribute %2.").arg(formatKeyword(declaration->displayName(m_namePool)))
799                                                                                        .arg(formatKeyword(m_namePool->displayName(attributeName))));
800                     return false;
801                 }
802             }
803         }
804     }
805 
806     // 4
807     // so what?...
808 
809     // 5
810     // hmm...
811 
812     // 6
813     // TODO: check assertions
814 
815     // 7
816     // TODO: check type table restrictions
817 
818     // rememeber the type of that element node
819     m_model->setAssignedType(item().toNodeModelIndex(), type);
820 
821     return true;
822 }
823 
validateAttribute(const XsdAttributeUse::Ptr & declaration,const QString & value)824 bool XsdValidatingInstanceReader::validateAttribute(const XsdAttributeUse::Ptr &declaration, const QString &value)
825 {
826     const AnySimpleType::Ptr attributeType = declaration->attribute()->type();
827     const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(attributeType, m_context);
828 
829     const QString actualValue = XsdTypeChecker::normalizedValue(value, facets);
830 
831     QString errorMsg;
832     AnySimpleType::Ptr boundType;
833 
834     const QXmlNodeModelIndex index = attributeItem(declaration->attribute()->name(m_namePool)).toNodeModelIndex();
835 
836     const XsdTypeChecker checker(m_context, namespaceBindings(index), sourceLocation());
837     if (!checker.isValidString(actualValue, attributeType, errorMsg, &boundType)) {
838         error(QtXmlPatterns::tr("Content of attribute %1 does not match its type definition: %2.").arg(formatKeyword(declaration->attribute()->displayName(m_namePool))).arg(errorMsg));
839         return false;
840     }
841 
842     // @see http://www.w3.org/TR/xmlschema11-1/#cvc-au
843     if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdAttributeUse::ValueConstraint::Fixed) {
844         const QString actualConstraintValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
845         if (!checker.valuesAreEqual(actualValue, actualConstraintValue, attributeType)) {
846             error(QtXmlPatterns::tr("Content of attribute %1 does not match defined value constraint.").arg(formatKeyword(declaration->attribute()->displayName(m_namePool))));
847             return false;
848         }
849     }
850 
851     if (BuiltinTypes::xsID->wxsTypeMatches(declaration->attribute()->type())) {
852         addIdIdRefBinding(actualValue, declaration->attribute());
853     }
854 
855     if (m_idRefsType->wxsTypeMatches(declaration->attribute()->type())) {
856         const QStringList idRefs = actualValue.split(QLatin1Char(' '), Qt::SkipEmptyParts);
857         for (int i = 0; i < idRefs.count(); ++i)
858             m_idRefs.insert(idRefs.at(i));
859     } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(declaration->attribute()->type())) {
860         m_idRefs.insert(actualValue);
861     }
862 
863     m_model->setAssignedType(index, declaration->attribute()->type());
864     m_model->setAssignedAttribute(index, declaration->attribute());
865 
866     return true;
867 }
868 
869 //TODO: merge that with the method above
validateAttribute(const XsdAttribute::Ptr & declaration,const QString & value)870 bool XsdValidatingInstanceReader::validateAttribute(const XsdAttribute::Ptr &declaration, const QString &value)
871 {
872     const AnySimpleType::Ptr attributeType = declaration->type();
873     const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(attributeType, m_context);
874 
875     const QString actualValue = XsdTypeChecker::normalizedValue(value, facets);
876 
877     QString errorMsg;
878     AnySimpleType::Ptr boundType;
879 
880     const QXmlNodeModelIndex index = attributeItem(declaration->name(m_namePool)).toNodeModelIndex();
881 
882     const XsdTypeChecker checker(m_context, namespaceBindings(index), sourceLocation());
883     if (!checker.isValidString(actualValue, attributeType, errorMsg, &boundType)) {
884         error(QtXmlPatterns::tr("Content of attribute %1 does not match its type definition: %2.").arg(formatKeyword(declaration->displayName(m_namePool))).arg(errorMsg));
885         return false;
886     }
887 
888     // @see http://www.w3.org/TR/xmlschema11-1/#cvc-au
889     if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdAttribute::ValueConstraint::Fixed) {
890         const QString actualConstraintValue = XsdTypeChecker::normalizedValue(declaration->valueConstraint()->value(), facets);
891         if (!checker.valuesAreEqual(actualValue, actualConstraintValue, attributeType)) {
892             error(QtXmlPatterns::tr("Content of attribute %1 does not match defined value constraint.").arg(formatKeyword(declaration->displayName(m_namePool))));
893             return false;
894         }
895     }
896 
897     if (BuiltinTypes::xsID->wxsTypeMatches(declaration->type())) {
898         addIdIdRefBinding(actualValue, declaration);
899     }
900 
901     if (m_idRefsType->wxsTypeMatches(declaration->type())) {
902         const QStringList idRefs = actualValue.split(QLatin1Char(' '), Qt::SkipEmptyParts);
903         for (int i = 0; i < idRefs.count(); ++i)
904             m_idRefs.insert(idRefs.at(i));
905     } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(declaration->type())) {
906         m_idRefs.insert(actualValue);
907     }
908 
909     m_model->setAssignedType(index, declaration->type());
910     m_model->setAssignedAttribute(index, declaration);
911 
912     return true;
913 }
914 
validateAttributeWildcard(const QXmlName & attributeName,const XsdWildcard::Ptr & wildcard)915 bool XsdValidatingInstanceReader::validateAttributeWildcard(const QXmlName &attributeName, const XsdWildcard::Ptr &wildcard)
916 {
917     // @see http://www.w3.org/TR/xmlschema11-1/#cvc-wildcard
918 
919     // wildcards using XsdWildcard::absentNamespace, so we have to fix that here
920     QXmlName name(attributeName);
921     if (name.namespaceURI() == StandardNamespaces::empty) {
922         name.setNamespaceURI(m_namePool->allocateNamespace(XsdWildcard::absentNamespace()));
923     }
924 
925     return XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard, m_namePool);
926 }
927 
validateIdentityConstraint(const XsdElement::Ptr & element,const QXmlItem & currentItem)928 bool XsdValidatingInstanceReader::validateIdentityConstraint(const XsdElement::Ptr &element, const QXmlItem &currentItem)
929 {
930     const XsdIdentityConstraint::List constraints = element->identityConstraints();
931 
932     for (int i = 0; i < constraints.count(); ++i) {
933         const XsdIdentityConstraint::Ptr constraint = constraints.at(i);
934 
935         TargetNode::Set targetNodeSet, qualifiedNodeSet;
936         selectNodeSets(element, currentItem, constraint, targetNodeSet, qualifiedNodeSet);
937 
938         if (constraint->category() == XsdIdentityConstraint::Unique) {
939             if (!validateUniqueIdentityConstraint(element, constraint, qualifiedNodeSet))
940                 return false;
941         } else if (constraint->category() == XsdIdentityConstraint::Key) {
942             if (!validateKeyIdentityConstraint(element, constraint, targetNodeSet, qualifiedNodeSet))
943                 return false;
944         }
945     }
946 
947     // we do the keyref check in a separated run to make sure that all keys are available
948     for (int i = 0; i < constraints.count(); ++i) {
949         const XsdIdentityConstraint::Ptr constraint = constraints.at(i);
950         if (constraint->category() == XsdIdentityConstraint::KeyReference) {
951             TargetNode::Set targetNodeSet, qualifiedNodeSet;
952             selectNodeSets(element, currentItem, constraint, targetNodeSet, qualifiedNodeSet);
953 
954             if (!validateKeyRefIdentityConstraint(element, constraint, qualifiedNodeSet))
955                 return false;
956         }
957     }
958 
959     return true;
960 }
961 
validateUniqueIdentityConstraint(const XsdElement::Ptr &,const XsdIdentityConstraint::Ptr & constraint,const TargetNode::Set & qualifiedNodeSet)962 bool XsdValidatingInstanceReader::validateUniqueIdentityConstraint(const XsdElement::Ptr&, const XsdIdentityConstraint::Ptr &constraint, const TargetNode::Set &qualifiedNodeSet)
963 {
964     // @see http://www.w3.org/TR/xmlschema11-1/#d0e32243
965 
966     // 4.1
967     const XsdSchemaSourceLocationReflection reflection(sourceLocation());
968 
969     for (auto it = qualifiedNodeSet.cbegin(), end = qualifiedNodeSet.cend(); it != end; ++it) {
970         for (auto jt = qualifiedNodeSet.cbegin(); jt != it; ++jt) {
971             if (it->fieldsAreEqual(*jt, m_namePool, m_context, &reflection)) {
972                 error(QtXmlPatterns::tr("Non-unique value found for constraint %1.").arg(formatKeyword(constraint->displayName(m_namePool))));
973                 return false;
974             }
975         }
976     }
977 
978     m_idcKeys.insert(constraint->name(m_namePool), qualifiedNodeSet);
979 
980     return true;
981 }
982 
validateKeyIdentityConstraint(const XsdElement::Ptr & element,const XsdIdentityConstraint::Ptr & constraint,const TargetNode::Set & targetNodeSet,const TargetNode::Set & qualifiedNodeSet)983 bool XsdValidatingInstanceReader::validateKeyIdentityConstraint(const XsdElement::Ptr &element, const XsdIdentityConstraint::Ptr &constraint, const TargetNode::Set &targetNodeSet, const TargetNode::Set &qualifiedNodeSet)
984 {
985     // @see http://www.w3.org/TR/xmlschema11-1/#d0e32243
986 
987     // 4.2
988     const XsdSchemaSourceLocationReflection reflection(sourceLocation());
989 
990     // 4.2.1
991     if (targetNodeSet.count() != qualifiedNodeSet.count()) {
992         error(QtXmlPatterns::tr("Key constraint %1 contains absent fields.").arg(formatKeyword(constraint->displayName(m_namePool))));
993         return false;
994     }
995 
996     // 4.2.2
997     if (!validateUniqueIdentityConstraint(element, constraint, qualifiedNodeSet))
998         return false;
999 
1000     // 4.2.3
1001     for (const TargetNode node : qualifiedNodeSet) {
1002         const QVector<QXmlItem> fieldItems = node.fieldItems();
1003         for (int i = 0; i < fieldItems.count(); ++i) {
1004             const QXmlNodeModelIndex index = fieldItems.at(i).toNodeModelIndex();
1005             if (m_model->kind(index) == QXmlNodeModelIndex::Element) {
1006                 const XsdElement::Ptr declaration = m_model->assignedElement(index);
1007                 if (declaration && declaration->isNillable()) {
1008                     error(QtXmlPatterns::tr("Key constraint %1 contains references nillable element %2.")
1009                                            .arg(formatKeyword(constraint->displayName(m_namePool)))
1010                                            .arg(formatKeyword(declaration->displayName(m_namePool))));
1011                     return false;
1012                 }
1013             }
1014         }
1015     }
1016 
1017     m_idcKeys.insert(constraint->name(m_namePool), qualifiedNodeSet);
1018 
1019     return true;
1020 }
1021 
validateKeyRefIdentityConstraint(const XsdElement::Ptr &,const XsdIdentityConstraint::Ptr & constraint,const TargetNode::Set & qualifiedNodeSet)1022 bool XsdValidatingInstanceReader::validateKeyRefIdentityConstraint(const XsdElement::Ptr&, const XsdIdentityConstraint::Ptr &constraint, const TargetNode::Set &qualifiedNodeSet)
1023 {
1024     // @see http://www.w3.org/TR/xmlschema11-1/#d0e32243
1025 
1026     // 4.3
1027     const XsdSchemaSourceLocationReflection reflection(sourceLocation());
1028 
1029     const TargetNode::Set keySet = m_idcKeys.value(constraint->referencedKey()->name(m_namePool));
1030 
1031     for (const TargetNode &node : qualifiedNodeSet) {
1032 
1033         bool foundMatching = false;
1034 
1035         for (const TargetNode &keyNode : keySet) {
1036             if (node.fieldsAreEqual(keyNode, m_namePool, m_context, &reflection)) {
1037                 foundMatching = true;
1038                 break;
1039             }
1040         }
1041 
1042         if (!foundMatching) {
1043             error(QtXmlPatterns::tr("No referenced value found for key reference %1.").arg(formatKeyword(constraint->displayName(m_namePool))));
1044             return false;
1045         }
1046     }
1047 
1048     return true;
1049 }
1050 
createXQuery(const QList<QXmlName> & namespaceBindings,const QXmlItem & contextNode,const QString & queryString) const1051 QXmlQuery XsdValidatingInstanceReader::createXQuery(const QList<QXmlName> &namespaceBindings, const QXmlItem &contextNode, const QString &queryString) const
1052 {
1053     // create a public name pool from our name pool
1054     QXmlNamePool namePool(m_namePool.data());
1055 
1056     // the QXmlQuery shall work with the same name pool as we do
1057     QXmlQuery query(namePool);
1058 
1059     // add additional namespace bindings
1060     QXmlQueryPrivate *queryPrivate = query.d;
1061 
1062     for (int i = 0; i < namespaceBindings.count(); ++i) {
1063         if (namespaceBindings.at(i).prefix() != StandardPrefixes::empty)
1064             queryPrivate->addAdditionalNamespaceBinding(namespaceBindings.at(i));
1065     }
1066 
1067     // set the context node for that query and the query string
1068     query.setFocus(contextNode);
1069     query.setQuery(queryString, m_documentUri);
1070 
1071     return query;
1072 }
1073 
selectNodeSets(const XsdElement::Ptr &,const QXmlItem & currentItem,const XsdIdentityConstraint::Ptr & constraint,TargetNode::Set & targetNodeSet,TargetNode::Set & qualifiedNodeSet)1074 bool XsdValidatingInstanceReader::selectNodeSets(const XsdElement::Ptr&, const QXmlItem &currentItem, const XsdIdentityConstraint::Ptr &constraint, TargetNode::Set &targetNodeSet, TargetNode::Set &qualifiedNodeSet)
1075 {
1076     // at first select all target nodes
1077     const XsdXPathExpression::Ptr selector = constraint->selector();
1078     const XsdXPathExpression::List fields = constraint->fields();
1079 
1080     QXmlQuery query = createXQuery(selector->namespaceBindings(), currentItem, selector->expression());
1081 
1082     QXmlResultItems resultItems;
1083     query.evaluateTo(&resultItems);
1084 
1085     // now we iterate over all target nodes and select the fields for each node
1086     QXmlItem item(resultItems.next());
1087     while (!item.isNull()) {
1088 
1089         TargetNode targetNode(item);
1090 
1091         for (int i = 0; i < fields.count(); ++i) {
1092             const XsdXPathExpression::Ptr field = fields.at(i);
1093             QXmlQuery fieldQuery = createXQuery(field->namespaceBindings(), item, field->expression());
1094 
1095             QXmlResultItems fieldResultItems;
1096             fieldQuery.evaluateTo(&fieldResultItems);
1097 
1098             // copy result into vetor for better testing...
1099             QVector<QXmlItem> fieldVector;
1100             QXmlItem fieldItem(fieldResultItems.next());
1101             while (!fieldItem.isNull()) {
1102                 fieldVector.append(fieldItem);
1103                 fieldItem = fieldResultItems.next();
1104             }
1105 
1106             if (fieldVector.count() > 1) {
1107                 error(QtXmlPatterns::tr("More than one value found for field %1.").arg(formatData(field->expression())));
1108                 return false;
1109             }
1110 
1111             if (fieldVector.count() == 1) {
1112                 fieldItem = fieldVector.first();
1113 
1114                 const QXmlNodeModelIndex index = fieldItem.toNodeModelIndex();
1115                 const SchemaType::Ptr type = m_model->assignedType(index);
1116                 Q_ASSERT(type);
1117 
1118                 bool typeOk = true;
1119                 if (type->isComplexType()) {
1120                     if (type->isDefinedBySchema()) {
1121                         if (XsdComplexType::Ptr(type)->contentType()->variety() != XsdComplexType::ContentType::Simple)
1122                             typeOk = false;
1123                     } else {
1124                         typeOk = false;
1125                     }
1126                 }
1127                 if (!typeOk) {
1128                     error(QtXmlPatterns::tr("Field %1 has no simple type.").arg(formatData(field->expression())));
1129                     return false;
1130                 }
1131 
1132                 SchemaType::Ptr targetType = type;
1133                 QString value = m_model->stringValue(fieldItem.toNodeModelIndex());
1134 
1135                 if (type->isDefinedBySchema()) {
1136                     if (type->isSimpleType())
1137                         targetType = XsdSimpleType::Ptr(type)->primitiveType();
1138                     else
1139                         targetType = XsdComplexType::Ptr(type)->contentType()->simpleType();
1140 
1141                     if (!targetType) {
1142                         // QTBUG-77620: pattern type within a union doesn't get
1143                         // its primitive type set.  FIXME: find root cause and
1144                         // fix that, so we can remove this (and an XFAIL).
1145                         error(QtXmlPatterns::tr("Field %1 is missing its simple type.")
1146                                                         .arg(formatData(field->expression())));
1147                         return false;
1148                     }
1149                 } else {
1150                     if (BuiltinTypes::xsAnySimpleType->name(m_namePool) == type->name(m_namePool)) {
1151                         targetType = BuiltinTypes::xsString;
1152                         value = QLatin1String("___anySimpleType_value");
1153                     }
1154                 }
1155 
1156                 // if it is xs:QName derived type, we normalize the name content
1157                 // and do a string comparison
1158                 if (BuiltinTypes::xsQName->wxsTypeMatches(type)) {
1159                     targetType = BuiltinTypes::xsString;
1160 
1161                     const QXmlName qName = convertToQName(value.trimmed());
1162                     value = QString::fromLatin1("%1:%2").arg(m_namePool->stringForNamespace(qName.namespaceURI())).arg(m_namePool->stringForLocalName(qName.localName()));
1163                 }
1164 
1165                 targetNode.addField(fieldItem, value, targetType);
1166             } else {
1167                 // we add an empty entry here, that makes comparison easier later on
1168                 targetNode.addField(QXmlItem(), QString(), SchemaType::Ptr());
1169             }
1170         }
1171 
1172         targetNodeSet.insert(targetNode);
1173 
1174         item = resultItems.next();
1175     }
1176 
1177     // copy all items from target node set to qualified node set, that have no empty fields
1178     for (const TargetNode &node : qAsConst(targetNodeSet)) {
1179         if (node.emptyFieldsCount() == 0)
1180             qualifiedNodeSet.insert(node);
1181     }
1182 
1183     return true;
1184 }
1185 
elementByName(const QXmlName & name) const1186 XsdElement::Ptr XsdValidatingInstanceReader::elementByName(const QXmlName &name) const
1187 {
1188     return m_schema->element(name);
1189 }
1190 
attributeByName(const QXmlName & name) const1191 XsdAttribute::Ptr XsdValidatingInstanceReader::attributeByName(const QXmlName &name) const
1192 {
1193     return m_schema->attribute(name);
1194 }
1195 
typeByName(const QXmlName & name) const1196 SchemaType::Ptr XsdValidatingInstanceReader::typeByName(const QXmlName &name) const
1197 {
1198     const SchemaType::Ptr type = m_schema->type(name);
1199     if (type)
1200         return type;
1201 
1202     return m_context->schemaTypeFactory()->createSchemaType(name);
1203 }
1204 
addIdIdRefBinding(const QString & id,const NamedSchemaComponent::Ptr & binding)1205 void XsdValidatingInstanceReader::addIdIdRefBinding(const QString &id, const NamedSchemaComponent::Ptr &binding)
1206 {
1207     if (!m_model->idIdRefBindings(id).isEmpty()) {
1208         error(QtXmlPatterns::tr("ID value '%1' is not unique.").arg(formatKeyword(id)));
1209         return;
1210     }
1211 
1212     m_model->addIdIdRefBinding(id, binding);
1213 }
1214 
qNameAttribute(const QXmlName & attributeName)1215 QString XsdValidatingInstanceReader::qNameAttribute(const QXmlName &attributeName)
1216 {
1217     const QString value = attribute(attributeName).simplified();
1218     if (!XPathHelper::isQName(value)) {
1219         error(QtXmlPatterns::tr("'%1' attribute contains invalid QName content: %2.").arg(m_namePool->displayName(attributeName)).arg(formatData(value)));
1220         return QString();
1221     } else {
1222         return value;
1223     }
1224 }
1225 
anyType()1226 XsdComplexType::Ptr XsdValidatingInstanceReader::anyType()
1227 {
1228     if (m_anyType)
1229         return m_anyType;
1230 
1231     const XsdWildcard::Ptr wildcard(new XsdWildcard());
1232     wildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
1233     wildcard->setProcessContents(XsdWildcard::Lax);
1234 
1235     const XsdParticle::Ptr outerParticle(new XsdParticle());
1236     outerParticle->setMinimumOccurs(1);
1237     outerParticle->setMaximumOccurs(1);
1238 
1239     const XsdParticle::Ptr innerParticle(new XsdParticle());
1240     innerParticle->setMinimumOccurs(0);
1241     innerParticle->setMaximumOccursUnbounded(true);
1242     innerParticle->setTerm(wildcard);
1243 
1244     const XsdModelGroup::Ptr outerModelGroup(new XsdModelGroup());
1245     outerModelGroup->setCompositor(XsdModelGroup::SequenceCompositor);
1246     outerModelGroup->setParticles(XsdParticle::List() << innerParticle);
1247     outerParticle->setTerm(outerModelGroup);
1248 
1249     m_anyType = XsdComplexType::Ptr(new XsdComplexType());
1250     m_anyType->setName(BuiltinTypes::xsAnyType->name(m_namePool));
1251     m_anyType->setDerivationMethod(XsdComplexType::DerivationRestriction);
1252     m_anyType->contentType()->setVariety(XsdComplexType::ContentType::Mixed);
1253     m_anyType->contentType()->setParticle(outerParticle);
1254     m_anyType->setAttributeWildcard(wildcard);
1255     m_anyType->setIsAbstract(false);
1256 
1257     return m_anyType;
1258 }
1259 
1260 QT_END_NAMESPACE
1261