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
71QT_BEGIN_NAMESPACE
72
73using namespace QPatternist;
74
75namespace QPatternist
76{
77 template <>
78 template <>
79 bool XsdStateMachine<XsdTerm::Ptr>::inputEqualsTransition<QXmlName>(QXmlName name, XsdTerm::Ptr term) const
80 {
81 if (term->isElement()) {
82 return (XsdElement::Ptr(term)->name(namePool: 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(uri: XsdWildcard::absentNamespace()));
87 }
88
89 return XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard: XsdWildcard::Ptr(term), namePool: m_namePool);
90 }
91
92 return false;
93 }
94}
95
96XsdValidatingInstanceReader::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(uri: CommonNamespaces::XSI, localName: QLatin1String("nil")))
101 , m_xsiTypeName(m_namePool->allocateQName(uri: CommonNamespaces::XSI, localName: QLatin1String("type")))
102 , m_xsiSchemaLocationName(m_namePool->allocateQName(uri: CommonNamespaces::XSI, localName: QLatin1String("schemaLocation")))
103 , m_xsiNoNamespaceSchemaLocationName(m_namePool->allocateQName(uri: CommonNamespaces::XSI, localName: QLatin1String("noNamespaceSchemaLocation")))
104 , m_documentUri(documentUri)
105{
106 m_idRefsType = m_context->schemaTypeFactory()->createSchemaType(name: m_namePool->allocateQName(uri: CommonNamespaces::WXS, localName: QLatin1String("IDREFS")));
107}
108
109void XsdValidatingInstanceReader::addSchema(const XsdSchema::Ptr &schema, const QUrl &locationUrl)
110{
111 if (!m_mergedSchemas.contains(akey: locationUrl)) {
112 m_mergedSchemas.insert(akey: locationUrl, avalue: QStringList() << schema->targetNamespace());
113 } else {
114 QStringList &targetNamespaces = m_mergedSchemas[locationUrl];
115 if (targetNamespaces.contains(str: schema->targetNamespace()))
116 return;
117
118 targetNamespaces.append(t: 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
129bool 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, element&: processedElement))
144 return false;
145
146 read();
147
148 if (processedElement) { // for wildcard with 'skip' we have no element
149 m_model->setAssignedElement(index: currentItem.toNodeModelIndex(), element: processedElement);
150
151 // check identity constraints after all child nodes have been
152 // validated, so that we know there assigned types
153 validateIdentityConstraint(element: processedElement, currentItem);
154 }
155
156 if (!m_stateMachines.isEmpty() && hasStateMachine) {
157 if (!m_stateMachines.top().inEndState()) {
158 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 is missing child element.").arg(a: formatKeyword(keyword: m_namePool->displayName(qName: 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(t&: m_idRefs)) {
171 if (!ids.contains(str: id)) {
172 error(msg: QtXmlPatterns::tr(sourceText: "There is one IDREF value with no corresponding ID: %1.").arg(a: formatKeyword(keyword: id)));
173 return false;
174 }
175 }
176
177 return true;
178}
179
180void XsdValidatingInstanceReader::error(const QString &msg) const
181{
182 m_context.data()->error(message: msg, errorCode: XsdSchemaContext::XSDError, sourceLocation: sourceLocation());
183}
184
185bool XsdValidatingInstanceReader::loadSchema(const QString &targetNamespace, const QUrl &location)
186{
187 const AutoPtr<QNetworkReply> reply(AccelTreeResourceLoader::load(uri: location, networkManager: m_context->networkAccessManager(),
188 context: m_context, handling: 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(source: reply.data(), documentUri: location, targetNamespace);
198 if (!schema.isValid()) {
199 error(msg: QtXmlPatterns::tr(sourceText: "Loaded schema file is invalid."));
200 return false;
201 }
202
203 addSchema(schema: schema.m_schemaParserContext->schema(), locationUrl: location);
204
205 return true;
206}
207
208bool XsdValidatingInstanceReader::validate(bool &hasStateMachine, XsdElement::Ptr &processedElement)
209{
210 // first check if a custom schema is defined
211 if (hasAttribute(name: m_xsiSchemaLocationName)) {
212 const QString schemaLocation = attribute(name: m_xsiSchemaLocationName);
213 const QStringList parts = schemaLocation.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
214 if ((parts.count()%2) == 1) {
215 error(msg: QtXmlPatterns::tr(sourceText: "%1 contains invalid data.").arg(a: formatKeyword(np: m_namePool, name: m_xsiSchemaLocationName)));
216 return false;
217 }
218
219 for (int i = 0; i < parts.count(); i += 2) {
220 const QString identifier = QString::fromLatin1(str: "%1 %2").arg(a: parts.at(i)).arg(a: parts.at(i: i + 1));
221 if (m_processedSchemaLocations.contains(value: identifier))
222 continue;
223 else
224 m_processedSchemaLocations.insert(value: 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(value: parts.at(i))) {
228 error(msg: QtXmlPatterns::tr(sourceText: "xsi:schemaLocation namespace %1 has already appeared earlier in the instance document.").arg(a: formatKeyword(keyword: parts.at(i))));
229 return false;
230 }
231
232 QUrl url(parts.at(i: i + 1));
233 if (url.isRelative()) {
234 Q_ASSERT(m_documentUri.isValid());
235
236 url = m_documentUri.resolved(relative: url);
237 }
238
239 loadSchema(targetNamespace: parts.at(i), location: url);
240 }
241 }
242
243 if (hasAttribute(name: m_xsiNoNamespaceSchemaLocationName)) {
244 const QString schemaLocation = attribute(name: m_xsiNoNamespaceSchemaLocationName);
245
246 if (!m_processedSchemaLocations.contains(value: schemaLocation)) {
247 m_processedSchemaLocations.insert(value: schemaLocation);
248
249 if (m_processedNamespaces.contains(value: QString())) {
250 error(msg: QtXmlPatterns::tr(sourceText: "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(relative: url);
259 }
260
261 loadSchema(targetNamespace: QString(), location: url);
262 }
263 }
264
265 m_processedNamespaces.insert(value: m_namePool->stringForNamespace(code: name().namespaceURI()));
266
267 if (!m_schema) {
268 error(msg: QtXmlPatterns::tr(sourceText: "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: name());
276 if (!element) {
277 if (!hasAttribute(name: m_xsiTypeName)) {
278 error(msg: QtXmlPatterns::tr(sourceText: "No definition for element %1 available.").arg(a: formatKeyword(np: m_namePool, name: 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(name: m_xsiNilName));
290
291 const QString type = qNameAttribute(attributeName: m_xsiTypeName);
292 const QXmlName typeName = convertToQName(name: type);
293
294 const SchemaType::Ptr elementType = typeByName(name: typeName);
295 if (!elementType) {
296 error(msg: QtXmlPatterns::tr(sourceText: "Specified type %1 is not known to the schema.").arg(a: formatType(np: m_namePool, name: typeName)));
297 return false;
298 }
299 element->setType(elementType);
300 }
301
302 // rememeber the element we process
303 processedElement = element;
304
305 if (!validateElement(declaration: element, hasStateMachine)) {
306 return false;
307 }
308
309 } else {
310 if (!m_stateMachines.top().proceed<QXmlName>(input: name())) {
311 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 is not defined in this scope.").arg(a: formatKeyword(np: m_namePool, name: 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(declaration: 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: name());
329 if (!elementDeclaration) {
330 if (hasAttribute(name: 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(name: m_xsiNilName));
339
340 const QString type = qNameAttribute(attributeName: m_xsiTypeName);
341 const QXmlName typeName = convertToQName(name: type);
342
343 const SchemaType::Ptr elementType = typeByName(name: typeName);
344 if (!elementType) {
345 error(msg: QtXmlPatterns::tr(sourceText: "Specified type %1 is not known to the schema.").arg(a: formatType(np: m_namePool, name: typeName)));
346 return false;
347 }
348 elementDeclaration->setType(elementType);
349 }
350 }
351
352 if (!elementDeclaration) {
353 if (wildcard->processContents() == XsdWildcard::Strict) {
354 error(msg: QtXmlPatterns::tr(sourceText: "Declaration for element %1 does not exist.").arg(a: formatKeyword(keyword: m_namePool->displayName(qName: 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(particle: anyType()->contentType()->particle());
361 hasStateMachine = true;
362 }
363 } else {
364 if (!validateElement(declaration: elementDeclaration, hasStateMachine)) {
365 if (wildcard->processContents() == XsdWildcard::Strict) {
366 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains invalid content.").arg(a: formatKeyword(keyword: m_namePool->displayName(qName: name()))));
367 return false;
368 }
369 }
370
371 // rememeber the type of that element node
372 m_model->setAssignedType(index: item().toNodeModelIndex(), type: 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(particle: outerParticle);
397 hasStateMachine = true;
398 }
399 }
400 }
401
402 return true;
403}
404
405void 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(state: 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(t: stateMachine);
427}
428
429bool 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(msg: QtXmlPatterns::tr(sourceText: "Element %1 is declared as abstract.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
440 return false;
441 }
442
443 // 3
444 if (!declaration->isNillable()) {
445 if (hasAttribute(name: m_xsiNilName)) {
446 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 is not nillable.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
447 return false; // 3.1
448 }
449 } else {
450 if (hasAttribute(name: m_xsiNilName)) {
451 const QString value = attribute(name: m_xsiNilName);
452 const Boolean::Ptr nil = Boolean::fromLexical(val: value);
453 if (nil->hasError()) {
454 error(msg: QtXmlPatterns::tr(sourceText: "Attribute %1 contains invalid data: %2").arg(a: formatKeyword(keyword: QLatin1String("nil."))).arg(a: formatData(data: 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(msg: QtXmlPatterns::tr(sourceText: "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(msg: QtXmlPatterns::tr(sourceText: "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(name: m_xsiTypeName)) {
481 const QString type = qNameAttribute(attributeName: m_xsiTypeName);
482 const QXmlName typeName = convertToQName(name: type);
483
484 const SchemaType::Ptr elementType = typeByName(name: typeName);
485 // 4.1
486 if (!elementType) {
487 error(msg: QtXmlPatterns::tr(sourceText: "Specified type %1 is not known to the schema.").arg(a: formatType(np: m_namePool, name: 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(type: elementType, otherType: declaration->type(), constraints)) {
499 if (declaration->type()->name(np: m_namePool) != BuiltinTypes::xsAnyType->name(np: m_namePool)) { // xs:anyType is a valid substitutable type here
500 error(msg: QtXmlPatterns::tr(sourceText: "Specified type %1 is not validly substitutable with element type %2.").arg(a: formatType(np: m_namePool, type: elementType)).arg(a: formatType(np: m_namePool, type: declaration->type())));
501 return false;
502 }
503 }
504
505 finalElementType = elementType;
506 }
507
508 if (!validateElementType(declaration, type: finalElementType, isNilled, hasStateMachine))
509 return false;
510
511 return true;
512}
513
514bool 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(msg: QtXmlPatterns::tr(sourceText: "Complex type %1 is not allowed to be abstract.").arg(a: formatType(np: 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
535bool 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(other: allowedAttributes);
543 if (!elementAttributes.isEmpty()) {
544 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains not allowed attributes.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
545 return false;
546 }
547
548 // 3.1.2
549 if (hasChildElement()) {
550 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains not allowed child element.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
551 return false;
552 }
553
554 // 3.1.3
555 if (!isNilled) {
556 const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type, context: m_context);
557
558 QString actualValue;
559 if (hasChildText()) {
560 actualValue = XsdTypeChecker::normalizedValue(value: text(), facets);
561 } else {
562 if (declaration->valueConstraint())
563 actualValue = XsdTypeChecker::normalizedValue(value: declaration->valueConstraint()->value(), facets);
564 }
565
566 QString errorMsg;
567 AnySimpleType::Ptr boundType;
568
569 const XsdTypeChecker checker(m_context, namespaceBindings(index: item().toNodeModelIndex()), sourceLocation());
570 if (!checker.isValidString(normalizedString: actualValue, type, errorMsg, boundType: &boundType)) {
571 error(msg: QtXmlPatterns::tr(sourceText: "Content of element %1 does not match its type definition: %2.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))).arg(a: 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(value: declaration->valueConstraint()->value(), facets);
578 if (!text().isEmpty() && !checker.valuesAreEqual(value: actualValue, otherValue: actualConstraintValue, type)) {
579 error(msg: QtXmlPatterns::tr(sourceText: "Content of element %1 does not match defined value constraint.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: 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(index: item().toNodeModelIndex(), type);
589
590 const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type, context: m_context);
591 const QString actualValue = XsdTypeChecker::normalizedValue(value: text(), facets);
592
593 if (BuiltinTypes::xsID->wxsTypeMatches(other: type)) {
594 addIdIdRefBinding(id: actualValue, binding: declaration);
595 }
596
597 if (m_idRefsType->wxsTypeMatches(other: type)) {
598 const QStringList idRefs = actualValue.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
599 for (int i = 0; i < idRefs.count(); ++i) {
600 m_idRefs.insert(value: idRefs.at(i));
601 }
602 } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(other: type)) {
603 m_idRefs.insert(value: actualValue);
604 }
605
606 return true;
607}
608
609static 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(other: uses.at(i)->attribute()->type()))
614 return true;
615 }
616
617 return false;
618}
619
620bool 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(np: m_namePool) == BuiltinTypes::xsAnyType->name(np: 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(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains not allowed child content.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
640 return false;
641 }
642 }
643
644 // 1.2
645 if (complexType->contentType()->variety() == XsdComplexType::ContentType::Simple) {
646 if (hasChildElement()) {
647 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains not allowed child element.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
648 return false;
649 }
650
651 const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type: complexType->contentType()->simpleType(), context: m_context);
652 QString actualValue;
653 if (hasChildText()) {
654 actualValue = XsdTypeChecker::normalizedValue(value: text(), facets);
655 } else {
656 if (declaration->valueConstraint())
657 actualValue = XsdTypeChecker::normalizedValue(value: declaration->valueConstraint()->value(), facets);
658 }
659
660 QString errorMsg;
661 AnySimpleType::Ptr boundType;
662 const XsdTypeChecker checker(m_context, namespaceBindings(index: item().toNodeModelIndex()), sourceLocation());
663 if (!checker.isValidString(normalizedString: actualValue, type: complexType->contentType()->simpleType(), errorMsg, boundType: &boundType)) {
664 error(msg: QtXmlPatterns::tr(sourceText: "Content of element %1 does not match its type definition: %2.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))).arg(a: errorMsg));
665 return false;
666 }
667
668 // additional check
669 if (declaration->valueConstraint() && declaration->valueConstraint()->variety() == XsdElement::ValueConstraint::Fixed) {
670 if (!checker.valuesAreEqual(value: actualValue, otherValue: declaration->valueConstraint()->value(), type: boundType)) {
671 error(msg: QtXmlPatterns::tr(sourceText: "Content of element %1 does not match defined value constraint.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: 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(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains not allowed text content.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: 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(particle: 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(msg: QtXmlPatterns::tr(sourceText: "Element %1 cannot contain other elements, as it has a fixed content.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
699 return false;
700 }
701
702 const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type: complexType->contentType()->simpleType(), context: m_context);
703 QString actualValue;
704 if (hasChildText()) {
705 actualValue = XsdTypeChecker::normalizedValue(value: text(), facets);
706 } else {
707 if (declaration->valueConstraint())
708 actualValue = XsdTypeChecker::normalizedValue(value: declaration->valueConstraint()->value(), facets);
709 }
710
711 if (actualValue != declaration->valueConstraint()->value()) {
712 error(msg: QtXmlPatterns::tr(sourceText: "Content of element %1 does not match defined value constraint.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: 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(akey: attributeUses.at(i)->attribute()->name(namePool: m_namePool), avalue: 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(value: it.key())) {
738 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 is missing required attribute %2.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool)))
739 .arg(a: formatKeyword(keyword: m_namePool->displayName(qName: it.key()))));
740 return false;
741 }
742 }
743 }
744
745 bool hasIDAttribute = hasIDAttributeUse(uses: 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(akey: attributeName) && (attributeUseHash.value(akey: attributeName)->useType() != XsdAttributeUse::ProhibitedUse)) {
759 if (!validateAttribute(declaration: attributeUseHash.value(akey: attributeName), value: attribute(name: 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(msg: QtXmlPatterns::tr(sourceText: "Attribute %1 does not match the attribute wildcard.").arg(a: formatKeyword(keyword: m_namePool->displayName(qName: attributeName))));
766 return false;
767 }
768
769 if (wildcard->processContents() != XsdWildcard::Skip) {
770 const XsdAttribute::Ptr attributeDeclaration = attributeByName(name: attributeName);
771
772 if (!attributeDeclaration) {
773 if (wildcard->processContents() == XsdWildcard::Strict) {
774 error(msg: QtXmlPatterns::tr(sourceText: "Declaration for attribute %1 does not exist.").arg(a: formatKeyword(keyword: m_namePool->displayName(qName: attributeName))));
775 return false;
776 }
777 } else {
778 if (BuiltinTypes::xsID->wxsTypeMatches(other: attributeDeclaration->type())) {
779 if (hasIDAttribute) {
780 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains two attributes of type %2.")
781 .arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool)))
782 .arg(a: formatKeyword(keyword: "ID")));
783 return false;
784 }
785
786 hasIDAttribute = true;
787 }
788
789 if (!validateAttribute(declaration: attributeDeclaration, value: attribute(name: attributeName))) {
790 if (wildcard->processContents() == XsdWildcard::Strict) {
791 error(msg: QtXmlPatterns::tr(sourceText: "Attribute %1 contains invalid content.").arg(a: formatKeyword(keyword: m_namePool->displayName(qName: attributeName))));
792 return false;
793 }
794 }
795 }
796 }
797 } else {
798 error(msg: QtXmlPatterns::tr(sourceText: "Element %1 contains unknown attribute %2.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool)))
799 .arg(a: formatKeyword(keyword: m_namePool->displayName(qName: 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(index: item().toNodeModelIndex(), type);
820
821 return true;
822}
823
824bool 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(type: attributeType, context: 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(name: declaration->attribute()->name(namePool: m_namePool)).toNodeModelIndex();
835
836 const XsdTypeChecker checker(m_context, namespaceBindings(index), sourceLocation());
837 if (!checker.isValidString(normalizedString: actualValue, type: attributeType, errorMsg, boundType: &boundType)) {
838 error(msg: QtXmlPatterns::tr(sourceText: "Content of attribute %1 does not match its type definition: %2.").arg(a: formatKeyword(keyword: declaration->attribute()->displayName(namePool: m_namePool))).arg(a: 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(value: declaration->valueConstraint()->value(), facets);
845 if (!checker.valuesAreEqual(value: actualValue, otherValue: actualConstraintValue, type: attributeType)) {
846 error(msg: QtXmlPatterns::tr(sourceText: "Content of attribute %1 does not match defined value constraint.").arg(a: formatKeyword(keyword: declaration->attribute()->displayName(namePool: m_namePool))));
847 return false;
848 }
849 }
850
851 if (BuiltinTypes::xsID->wxsTypeMatches(other: declaration->attribute()->type())) {
852 addIdIdRefBinding(id: actualValue, binding: declaration->attribute());
853 }
854
855 if (m_idRefsType->wxsTypeMatches(other: declaration->attribute()->type())) {
856 const QStringList idRefs = actualValue.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
857 for (int i = 0; i < idRefs.count(); ++i)
858 m_idRefs.insert(value: idRefs.at(i));
859 } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(other: declaration->attribute()->type())) {
860 m_idRefs.insert(value: actualValue);
861 }
862
863 m_model->setAssignedType(index, type: declaration->attribute()->type());
864 m_model->setAssignedAttribute(index, attribute: declaration->attribute());
865
866 return true;
867}
868
869//TODO: merge that with the method above
870bool XsdValidatingInstanceReader::validateAttribute(const XsdAttribute::Ptr &declaration, const QString &value)
871{
872 const AnySimpleType::Ptr attributeType = declaration->type();
873 const XsdFacet::Hash facets = XsdTypeChecker::mergedFacetsForType(type: attributeType, context: 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(name: declaration->name(namePool: m_namePool)).toNodeModelIndex();
881
882 const XsdTypeChecker checker(m_context, namespaceBindings(index), sourceLocation());
883 if (!checker.isValidString(normalizedString: actualValue, type: attributeType, errorMsg, boundType: &boundType)) {
884 error(msg: QtXmlPatterns::tr(sourceText: "Content of attribute %1 does not match its type definition: %2.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))).arg(a: 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(value: declaration->valueConstraint()->value(), facets);
891 if (!checker.valuesAreEqual(value: actualValue, otherValue: actualConstraintValue, type: attributeType)) {
892 error(msg: QtXmlPatterns::tr(sourceText: "Content of attribute %1 does not match defined value constraint.").arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
893 return false;
894 }
895 }
896
897 if (BuiltinTypes::xsID->wxsTypeMatches(other: declaration->type())) {
898 addIdIdRefBinding(id: actualValue, binding: declaration);
899 }
900
901 if (m_idRefsType->wxsTypeMatches(other: declaration->type())) {
902 const QStringList idRefs = actualValue.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts);
903 for (int i = 0; i < idRefs.count(); ++i)
904 m_idRefs.insert(value: idRefs.at(i));
905 } else if (BuiltinTypes::xsIDREF->wxsTypeMatches(other: declaration->type())) {
906 m_idRefs.insert(value: actualValue);
907 }
908
909 m_model->setAssignedType(index, type: declaration->type());
910 m_model->setAssignedAttribute(index, attribute: declaration);
911
912 return true;
913}
914
915bool 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(uri: XsdWildcard::absentNamespace()));
923 }
924
925 return XsdSchemaHelper::wildcardAllowsExpandedName(name, wildcard, namePool: m_namePool);
926}
927
928bool 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
962bool 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(other: *jt, namePool: m_namePool, context: m_context, reflection: &reflection)) {
972 error(msg: QtXmlPatterns::tr(sourceText: "Non-unique value found for constraint %1.").arg(a: formatKeyword(keyword: constraint->displayName(namePool: m_namePool))));
973 return false;
974 }
975 }
976 }
977
978 m_idcKeys.insert(akey: constraint->name(namePool: m_namePool), avalue: qualifiedNodeSet);
979
980 return true;
981}
982
983bool 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(msg: QtXmlPatterns::tr(sourceText: "Key constraint %1 contains absent fields.").arg(a: formatKeyword(keyword: constraint->displayName(namePool: 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(ni: index) == QXmlNodeModelIndex::Element) {
1006 const XsdElement::Ptr declaration = m_model->assignedElement(index);
1007 if (declaration && declaration->isNillable()) {
1008 error(msg: QtXmlPatterns::tr(sourceText: "Key constraint %1 contains references nillable element %2.")
1009 .arg(a: formatKeyword(keyword: constraint->displayName(namePool: m_namePool)))
1010 .arg(a: formatKeyword(keyword: declaration->displayName(namePool: m_namePool))));
1011 return false;
1012 }
1013 }
1014 }
1015 }
1016
1017 m_idcKeys.insert(akey: constraint->name(namePool: m_namePool), avalue: qualifiedNodeSet);
1018
1019 return true;
1020}
1021
1022bool 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(akey: constraint->referencedKey()->name(namePool: 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(other: keyNode, namePool: m_namePool, context: m_context, reflection: &reflection)) {
1037 foundMatching = true;
1038 break;
1039 }
1040 }
1041
1042 if (!foundMatching) {
1043 error(msg: QtXmlPatterns::tr(sourceText: "No referenced value found for key reference %1.").arg(a: formatKeyword(keyword: constraint->displayName(namePool: m_namePool))));
1044 return false;
1045 }
1046 }
1047
1048 return true;
1049}
1050
1051QXmlQuery 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(binding: namespaceBindings.at(i));
1065 }
1066
1067 // set the context node for that query and the query string
1068 query.setFocus(contextNode);
1069 query.setQuery(sourceCode: queryString, documentURI: m_documentUri);
1070
1071 return query;
1072}
1073
1074bool 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(namespaceBindings: selector->namespaceBindings(), contextNode: currentItem, queryString: selector->expression());
1081
1082 QXmlResultItems resultItems;
1083 query.evaluateTo(result: &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(namespaceBindings: field->namespaceBindings(), contextNode: item, queryString: field->expression());
1094
1095 QXmlResultItems fieldResultItems;
1096 fieldQuery.evaluateTo(result: &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(t: fieldItem);
1103 fieldItem = fieldResultItems.next();
1104 }
1105
1106 if (fieldVector.count() > 1) {
1107 error(msg: QtXmlPatterns::tr(sourceText: "More than one value found for field %1.").arg(a: formatData(data: 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(msg: QtXmlPatterns::tr(sourceText: "Field %1 has no simple type.").arg(a: formatData(data: field->expression())));
1129 return false;
1130 }
1131
1132 SchemaType::Ptr targetType = type;
1133 QString value = m_model->stringValue(n: 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(msg: QtXmlPatterns::tr(sourceText: "Field %1 is missing its simple type.")
1146 .arg(a: formatData(data: field->expression())));
1147 return false;
1148 }
1149 } else {
1150 if (BuiltinTypes::xsAnySimpleType->name(np: m_namePool) == type->name(np: 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(other: type)) {
1159 targetType = BuiltinTypes::xsString;
1160
1161 const QXmlName qName = convertToQName(name: value.trimmed());
1162 value = QString::fromLatin1(str: "%1:%2").arg(a: m_namePool->stringForNamespace(code: qName.namespaceURI())).arg(a: m_namePool->stringForLocalName(code: qName.localName()));
1163 }
1164
1165 targetNode.addField(item: fieldItem, data: value, type: targetType);
1166 } else {
1167 // we add an empty entry here, that makes comparison easier later on
1168 targetNode.addField(item: QXmlItem(), data: QString(), type: SchemaType::Ptr());
1169 }
1170 }
1171
1172 targetNodeSet.insert(value: 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(t&: targetNodeSet)) {
1179 if (node.emptyFieldsCount() == 0)
1180 qualifiedNodeSet.insert(value: node);
1181 }
1182
1183 return true;
1184}
1185
1186XsdElement::Ptr XsdValidatingInstanceReader::elementByName(const QXmlName &name) const
1187{
1188 return m_schema->element(name);
1189}
1190
1191XsdAttribute::Ptr XsdValidatingInstanceReader::attributeByName(const QXmlName &name) const
1192{
1193 return m_schema->attribute(name);
1194}
1195
1196SchemaType::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
1205void XsdValidatingInstanceReader::addIdIdRefBinding(const QString &id, const NamedSchemaComponent::Ptr &binding)
1206{
1207 if (!m_model->idIdRefBindings(id).isEmpty()) {
1208 error(msg: QtXmlPatterns::tr(sourceText: "ID value '%1' is not unique.").arg(a: formatKeyword(keyword: id)));
1209 return;
1210 }
1211
1212 m_model->addIdIdRefBinding(id, binding);
1213}
1214
1215QString XsdValidatingInstanceReader::qNameAttribute(const QXmlName &attributeName)
1216{
1217 const QString value = attribute(name: attributeName).simplified();
1218 if (!XPathHelper::isQName(qName: value)) {
1219 error(msg: QtXmlPatterns::tr(sourceText: "'%1' attribute contains invalid QName content: %2.").arg(a: m_namePool->displayName(qName: attributeName)).arg(a: formatData(data: value)));
1220 return QString();
1221 } else {
1222 return value;
1223 }
1224}
1225
1226XsdComplexType::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(np: 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
1260QT_END_NAMESPACE
1261

source code of qtxmlpatterns/src/xmlpatterns/schema/qxsdvalidatinginstancereader.cpp