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 ¤tItem)
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 ¤tItem, 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