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 "qxsdschemahelper_p.h"
41
42#include "qbuiltintypes_p.h"
43#include "qvaluefactory_p.h"
44#include "qxsdcomplextype_p.h"
45#include "qxsdmodelgroup_p.h"
46#include "qxsdsimpletype_p.h"
47#include "qxsdtypechecker_p.h"
48
49QT_BEGIN_NAMESPACE
50
51using namespace QPatternist;
52
53/*
54 * Calculates the effective total range minimum of the given @p particle as
55 * described by the algorithm in the schema spec.
56 */
57static inline unsigned int effectiveTotalRangeMinimum(const XsdParticle::Ptr &particle)
58{
59 const XsdModelGroup::Ptr group = particle->term();
60
61 if (group->compositor() == XsdModelGroup::ChoiceCompositor) {
62 // @see http://www.w3.org/TR/xmlschema11-1/# cos-choice-range
63
64 int minValue = -1;
65
66 const XsdParticle::List particles = group->particles();
67 if (particles.isEmpty())
68 minValue = 0;
69
70 for (int i = 0; i < particles.count(); ++i) {
71 const XsdParticle::Ptr particle = particles.at(i);
72
73 if (particle->term()->isElement() || particle->term()->isWildcard()) {
74 if (minValue == -1) {
75 minValue = particle->minimumOccurs();
76 } else {
77 minValue = qMin(a: (unsigned int)minValue, b: particle->minimumOccurs());
78 }
79 } else if (particle->term()->isModelGroup()) {
80 if (minValue == -1) {
81 minValue = effectiveTotalRangeMinimum(particle);
82 } else {
83 minValue = qMin(a: (unsigned int)minValue, b: effectiveTotalRangeMinimum(particle));
84 }
85 }
86 }
87
88 return (particle->minimumOccurs() * minValue);
89
90 } else {
91 // @see http://www.w3.org/TR/xmlschema11-1/# cos-seq-range
92
93 unsigned int sum = 0;
94 const XsdParticle::List particles = group->particles();
95 for (int i = 0; i < particles.count(); ++i) {
96 const XsdParticle::Ptr particle = particles.at(i);
97
98 if (particle->term()->isElement() || particle->term()->isWildcard())
99 sum += particle->minimumOccurs();
100 else if (particle->term()->isModelGroup())
101 sum += effectiveTotalRangeMinimum(particle);
102 }
103
104 return (particle->minimumOccurs() * sum);
105 }
106}
107
108bool XsdSchemaHelper::isParticleEmptiable(const XsdParticle::Ptr &particle)
109{
110 // @see http://www.w3.org/TR/xmlschema11-1/#cos-group-emptiable
111
112 if (particle->minimumOccurs() == 0)
113 return true;
114
115 if (!(particle->term()->isModelGroup()))
116 return false;
117
118 return (effectiveTotalRangeMinimum(particle) == 0);
119}
120
121bool XsdSchemaHelper::wildcardAllowsNamespaceName(const QString &nameSpace, const XsdWildcard::NamespaceConstraint::Ptr &constraint)
122{
123 // @see http://www.w3.org/TR/xmlschema11-1/#cvc-wildcard-namespace
124
125 // 1
126 if (constraint->variety() == XsdWildcard::NamespaceConstraint::Any)
127 return true;
128
129 // 2
130 if (constraint->variety() == XsdWildcard::NamespaceConstraint::Not) { // 2.1
131 if (!constraint->namespaces().contains(value: nameSpace)) // 2.2
132 if (nameSpace != XsdWildcard::absentNamespace()) // 2.3
133 return true;
134 }
135
136 // 3
137 if (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) {
138 if (constraint->namespaces().contains(value: nameSpace))
139 return true;
140 }
141
142 return false;
143}
144
145bool XsdSchemaHelper::wildcardAllowsExpandedName(const QXmlName &name, const XsdWildcard::Ptr &wildcard, const NamePool::Ptr &namePool)
146{
147 // @see http://www.w3.org/TR/xmlschema11-1/#cvc-wildcard-name
148
149 // 1
150 if (!wildcardAllowsNamespaceName(nameSpace: namePool->stringForNamespace(code: name.namespaceURI()), constraint: wildcard->namespaceConstraint()))
151 return false;
152
153 // 2, 3, 4
154 //TODO: we have no disallowed namespace yet
155
156 return true;
157}
158
159bool XsdSchemaHelper::isWildcardSubset(const XsdWildcard::Ptr &wildcard, const XsdWildcard::Ptr &otherWildcard)
160{
161 // @see http://www.w3.org/TR/xmlschema11-1/#cos-ns-subset
162 // wildcard =^ sub
163 // otherWildcard =^ super
164
165 const XsdWildcard::NamespaceConstraint::Ptr constraint(wildcard->namespaceConstraint());
166 const XsdWildcard::NamespaceConstraint::Ptr otherConstraint(otherWildcard->namespaceConstraint());
167
168 // 1
169 if (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Any)
170 return true;
171
172 // 2
173 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
174 if (otherConstraint->namespaces().contains(other: constraint->namespaces()))
175 return true;
176 }
177
178 // 3
179 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) {
180 if (!constraint->namespaces().intersects(other: otherConstraint->namespaces()))
181 return true;
182 }
183
184 // 4
185 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) {
186 if (constraint->namespaces().contains(other: otherConstraint->namespaces()))
187 return true;
188 }
189
190 return false;
191}
192
193XsdWildcard::Ptr XsdSchemaHelper::wildcardUnion(const XsdWildcard::Ptr &wildcard, const XsdWildcard::Ptr &otherWildcard)
194{
195 // @see http://www.w3.org/TR/xmlschema11-1/#cos-aw-union
196
197 XsdWildcard::Ptr unionWildcard(new XsdWildcard());
198
199 const XsdWildcard::NamespaceConstraint::Ptr constraint(wildcard->namespaceConstraint());
200 const XsdWildcard::NamespaceConstraint::Ptr otherConstraint(otherWildcard->namespaceConstraint());
201
202 // 1
203 if ((constraint->variety() == otherConstraint->variety()) &&
204 (constraint->namespaces() == otherConstraint->namespaces())) {
205 unionWildcard->namespaceConstraint()->setVariety(constraint->variety());
206 unionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces());
207 return unionWildcard;
208 }
209
210 // 2
211 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Any) || (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Any)) {
212 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
213 return unionWildcard;
214 }
215
216 // 3
217 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
218 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration);
219 unionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces() + otherConstraint->namespaces());
220 return unionWildcard;
221 }
222
223 // 4
224 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) {
225 if (constraint->namespaces() != otherConstraint->namespaces()) {
226 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not);
227 unionWildcard->namespaceConstraint()->setNamespaces(QSet<QString>() << XsdWildcard::absentNamespace());
228 return unionWildcard;
229 }
230 }
231
232 // 5
233 QSet<QString> sSet, negatedSet;
234 bool matches5 = false;
235 if (((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && !constraint->namespaces().contains(value: XsdWildcard::absentNamespace()))
236 && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
237
238 negatedSet = constraint->namespaces();
239 sSet = otherConstraint->namespaces();
240 matches5 = true;
241 } else if (((otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not) && !otherConstraint->namespaces().contains(value: XsdWildcard::absentNamespace()))
242 && (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
243
244 negatedSet = otherConstraint->namespaces();
245 sSet = constraint->namespaces();
246 matches5 = true;
247 }
248
249 if (matches5) {
250 if (sSet.contains(value: negatedSet.values().first()) && sSet.contains(value: XsdWildcard::absentNamespace())) { // 5.1
251 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
252 return unionWildcard;
253 }
254 if (sSet.contains(value: negatedSet.values().first()) && !sSet.contains(value: XsdWildcard::absentNamespace())) { // 5.2
255 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not);
256 unionWildcard->namespaceConstraint()->setNamespaces(QSet<QString>() << XsdWildcard::absentNamespace());
257 return unionWildcard;
258 }
259 if (!sSet.contains(value: negatedSet.values().first()) && sSet.contains(value: XsdWildcard::absentNamespace())) { // 5.3
260 return XsdWildcard::Ptr(); // not expressible
261 }
262 if (!sSet.contains(value: negatedSet.values().first()) && !sSet.contains(value: XsdWildcard::absentNamespace())) { // 5.4
263 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not);
264 unionWildcard->namespaceConstraint()->setNamespaces(negatedSet);
265 return unionWildcard;
266 }
267 }
268
269 // 6
270 bool matches6 = false;
271 if (((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) && constraint->namespaces().contains(value: XsdWildcard::absentNamespace()))
272 && (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
273
274 negatedSet = constraint->namespaces();
275 sSet = otherConstraint->namespaces();
276 matches6 = true;
277 } else if (((otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not) && otherConstraint->namespaces().contains(value: XsdWildcard::absentNamespace()))
278 && (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
279
280 negatedSet = otherConstraint->namespaces();
281 sSet = constraint->namespaces();
282 matches6 = true;
283 }
284
285 if (matches6) {
286 if (sSet.contains(value: XsdWildcard::absentNamespace())) { // 6.1
287 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
288 return unionWildcard;
289 }
290 if (!sSet.contains(value: XsdWildcard::absentNamespace())) { // 6.2
291 unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Not);
292 unionWildcard->namespaceConstraint()->setNamespaces(QSet<QString>() += XsdWildcard::absentNamespace());
293 return unionWildcard;
294 }
295 }
296
297 return XsdWildcard::Ptr();
298}
299
300XsdWildcard::Ptr XsdSchemaHelper::wildcardIntersection(const XsdWildcard::Ptr &wildcard, const XsdWildcard::Ptr &otherWildcard)
301{
302 // @see http://www.w3.org/TR/xmlschema11-1/#cos-aw-intersect
303
304 const XsdWildcard::NamespaceConstraint::Ptr constraint(wildcard->namespaceConstraint());
305 const XsdWildcard::NamespaceConstraint::Ptr otherConstraint(otherWildcard->namespaceConstraint());
306
307 const XsdWildcard::Ptr intersectionWildcard(new XsdWildcard());
308
309 // 1
310 if ((constraint->variety() == otherConstraint->variety()) &&
311 (constraint->namespaces() == otherConstraint->namespaces())) {
312 intersectionWildcard->namespaceConstraint()->setVariety(constraint->variety());
313 intersectionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces());
314 return intersectionWildcard;
315 }
316
317 // 2
318 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Any) &&
319 (otherConstraint->variety() != XsdWildcard::NamespaceConstraint::Any)) {
320 intersectionWildcard->namespaceConstraint()->setVariety(otherConstraint->variety());
321 intersectionWildcard->namespaceConstraint()->setNamespaces(otherConstraint->namespaces());
322 return intersectionWildcard;
323 }
324
325 // 2
326 if ((constraint->variety() != XsdWildcard::NamespaceConstraint::Any) &&
327 (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Any)) {
328 intersectionWildcard->namespaceConstraint()->setVariety(constraint->variety());
329 intersectionWildcard->namespaceConstraint()->setNamespaces(constraint->namespaces());
330 return intersectionWildcard;
331 }
332
333 // 3
334 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) &&
335 (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
336
337 QSet<QString> set = otherConstraint->namespaces();
338 set.subtract(other: constraint->namespaces());
339 set.remove(value: XsdWildcard::absentNamespace());
340
341 intersectionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration);
342 intersectionWildcard->namespaceConstraint()->setNamespaces(set);
343
344 return intersectionWildcard;
345 }
346
347 // 3
348 if ((otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not) &&
349 (constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
350
351 QSet<QString> set = constraint->namespaces();
352 set.subtract(other: otherConstraint->namespaces());
353 set.remove(value: XsdWildcard::absentNamespace());
354
355 intersectionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration);
356 intersectionWildcard->namespaceConstraint()->setNamespaces(set);
357
358 return intersectionWildcard;
359 }
360
361 // 4
362 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration) &&
363 (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Enumeration)) {
364
365 QSet<QString> set = constraint->namespaces();
366 set.intersect(other: otherConstraint->namespaces());
367
368 intersectionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Enumeration);
369 intersectionWildcard->namespaceConstraint()->setNamespaces(set);
370
371 return intersectionWildcard;
372 }
373
374 // 6
375 if ((constraint->variety() == XsdWildcard::NamespaceConstraint::Not) &&
376 (otherConstraint->variety() == XsdWildcard::NamespaceConstraint::Not)) {
377 if (!(constraint->namespaces().contains(value: XsdWildcard::absentNamespace())) && otherConstraint->namespaces().contains(value: XsdWildcard::absentNamespace())) {
378 return wildcard;
379 }
380 if (constraint->namespaces().contains(value: XsdWildcard::absentNamespace()) && !(otherConstraint->namespaces().contains(value: XsdWildcard::absentNamespace()))) {
381 return otherWildcard;
382 }
383 }
384
385 // 5 as not expressible return empty wildcard
386 return XsdWildcard::Ptr();
387}
388
389static SchemaType::DerivationConstraints convertBlockingConstraints(const NamedSchemaComponent::BlockingConstraints &constraints)
390{
391 SchemaType::DerivationConstraints result;
392
393 if (constraints & NamedSchemaComponent::RestrictionConstraint)
394 result |= SchemaType::RestrictionConstraint;
395 if (constraints & NamedSchemaComponent::ExtensionConstraint)
396 result |= SchemaType::ExtensionConstraint;
397
398 return result;
399}
400
401bool XsdSchemaHelper::isValidlySubstitutable(const SchemaType::Ptr &type, const SchemaType::Ptr &otherType, const SchemaType::DerivationConstraints &constraints)
402{
403 // @see http://www.w3.org/TR/xmlschema11-1/#key-val-sub-type
404
405 // 1
406 if (type->isComplexType() && otherType->isComplexType()) {
407 SchemaType::DerivationConstraints keywords = constraints;
408 if (otherType->isDefinedBySchema())
409 keywords |= convertBlockingConstraints(constraints: XsdComplexType::Ptr(otherType)->prohibitedSubstitutions());
410
411 return isComplexDerivationOk(derivedType: type, baseType: otherType, constraints: keywords);
412 }
413
414 // 2
415 if (type->isComplexType() && otherType->isSimpleType()) {
416 return isComplexDerivationOk(derivedType: type, baseType: otherType, constraints);
417 }
418
419 // 3
420 if (type->isSimpleType() && otherType->isSimpleType()) {
421 return isSimpleDerivationOk(derivedType: type, baseType: otherType, constraints);
422 }
423
424 return false;
425}
426
427bool XsdSchemaHelper::isSimpleDerivationOk(const SchemaType::Ptr &derivedType, const SchemaType::Ptr &baseType, const SchemaType::DerivationConstraints &constraints)
428{
429 // @see http://www.w3.org/TR/xmlschema11-1/#cos-st-derived-ok
430
431 // 1
432 if (derivedType == baseType)
433 return true;
434
435 // 2.1
436 if ((constraints & SchemaType::RestrictionConstraint) || derivedType->wxsSuperType()->derivationConstraints() & SchemaType::RestrictionConstraint) {
437 return false;
438 }
439
440 // 2.2.1
441 if (derivedType->wxsSuperType() == baseType)
442 return true;
443
444 // 2.2.2
445 if (derivedType->wxsSuperType() != BuiltinTypes::xsAnyType) {
446 if (isSimpleDerivationOk(derivedType: derivedType->wxsSuperType(), baseType, constraints))
447 return true;
448 }
449
450 // 2.2.3
451 if (derivedType->category() == SchemaType::SimpleTypeList || derivedType->category() == SchemaType::SimpleTypeUnion) {
452 if (baseType == BuiltinTypes::xsAnySimpleType)
453 return true;
454 }
455
456 // 2.2.4
457 if (baseType->category() == SchemaType::SimpleTypeUnion && baseType->isDefinedBySchema()) { // 2.2.4.1
458 const AnySimpleType::List memberTypes = XsdSimpleType::Ptr(baseType)->memberTypes();
459 for (int i = 0; i < memberTypes.count(); ++i) {
460 if (isSimpleDerivationOk(derivedType, baseType: memberTypes.at(i), constraints)) { // 2.2.4.2
461 if (XsdSimpleType::Ptr(baseType)->facets().isEmpty()) { // 2.2.4.3
462 return true;
463 }
464 }
465 }
466 }
467
468 return false;
469}
470
471bool XsdSchemaHelper::isComplexDerivationOk(const SchemaType::Ptr &derivedType, const SchemaType::Ptr &baseType, const SchemaType::DerivationConstraints &constraints)
472{
473 if (!derivedType)
474 return false;
475
476 // @see http://www.w3.org/TR/xmlschema11-1/#cos-ct-derived-ok
477
478 // 1
479 if (derivedType != baseType) {
480 if ((derivedType->derivationMethod() == SchemaType::DerivationRestriction) && (constraints & SchemaType::RestrictionConstraint))
481 return false;
482 if ((derivedType->derivationMethod() == SchemaType::DerivationExtension) && (constraints & SchemaType::ExtensionConstraint))
483 return false;
484 }
485
486 // 2.1
487 if (derivedType == baseType)
488 return true;
489
490 // 2.2
491 if (derivedType->wxsSuperType() == baseType)
492 return true;
493
494 // 2.3
495 bool isOk = true;
496 if (derivedType->wxsSuperType() == BuiltinTypes::xsAnyType) { // 2.3.1
497 isOk = false;
498 } else { // 2.3.2
499 if (!derivedType->wxsSuperType())
500 return false;
501
502 if (derivedType->wxsSuperType()->isComplexType()) { // 2.3.2.1
503 isOk = isComplexDerivationOk(derivedType: derivedType->wxsSuperType(), baseType, constraints);
504 } else { // 2.3.2.2
505 isOk = isSimpleDerivationOk(derivedType: derivedType->wxsSuperType(), baseType, constraints);
506 }
507 }
508 if (isOk)
509 return true;
510
511 return false;
512}
513
514bool XsdSchemaHelper::constructAndCompare(const DerivedString<TypeString>::Ptr &operand1,
515 const AtomicComparator::Operator op,
516 const DerivedString<TypeString>::Ptr &operand2,
517 const SchemaType::Ptr &type,
518 const ReportContext::Ptr &context,
519 const SourceLocationReflection *const sourceLocationReflection)
520{
521 Q_ASSERT_X(type && type->category() == SchemaType::SimpleTypeAtomic, Q_FUNC_INFO,
522 "We can only compare atomic values.");
523
524 // we can not cast a xs:String to a xs:QName, so lets go the safe way
525 if (type->name(np: context->namePool()) == BuiltinTypes::xsQName->name(np: context->namePool()))
526 return false;
527
528 const AtomicValue::Ptr value1 = ValueFactory::fromLexical(lexicalValue: operand1->stringValue(), type, context, sourceLocationReflection);
529 if (value1->hasError())
530 return false;
531
532 const AtomicValue::Ptr value2 = ValueFactory::fromLexical(lexicalValue: operand2->stringValue(), type, context, sourceLocationReflection);
533 if (value2->hasError())
534 return false;
535
536 return ComparisonFactory::compare(operand1: value1, op, operand2: value2, type, context, sourceLocationReflection);
537}
538
539bool XsdSchemaHelper::checkWildcardProcessContents(const XsdWildcard::Ptr &baseWildcard, const XsdWildcard::Ptr &derivedWildcard)
540{
541 if (baseWildcard->processContents() == XsdWildcard::Strict) {
542 if (derivedWildcard->processContents() == XsdWildcard::Lax || derivedWildcard->processContents() == XsdWildcard::Skip) {
543 return false;
544 }
545 } else if (baseWildcard->processContents() == XsdWildcard::Lax) {
546 if (derivedWildcard->processContents() == XsdWildcard::Skip)
547 return false;
548 }
549
550 return true;
551}
552
553bool XsdSchemaHelper::foundSubstitutionGroupTransitive(const XsdElement::Ptr &head, const XsdElement::Ptr &member, QSet<XsdElement::Ptr> &visitedElements)
554{
555 if (visitedElements.contains(value: member))
556 return false;
557 else
558 visitedElements.insert(value: member);
559
560 if (member->substitutionGroupAffiliations().isEmpty())
561 return false;
562
563 if (member->substitutionGroupAffiliations().contains(t: head)) {
564 return true;
565 } else {
566 const XsdElement::List affiliations = member->substitutionGroupAffiliations();
567 for (int i = 0; i < affiliations.count(); ++i) {
568 if (foundSubstitutionGroupTransitive(head, member: affiliations.at(i), visitedElements))
569 return true;
570 }
571
572 return false;
573 }
574}
575
576void XsdSchemaHelper::foundSubstitutionGroupTypeInheritance(const SchemaType::Ptr &headType, const SchemaType::Ptr &memberType,
577 QSet<SchemaType::DerivationMethod> &derivationSet, NamedSchemaComponent::BlockingConstraints &blockSet)
578{
579 if (!memberType)
580 return;
581
582 if (memberType == headType)
583 return;
584
585 derivationSet.insert(value: memberType->derivationMethod());
586
587 if (memberType->isComplexType()) {
588 const XsdComplexType::Ptr complexType(memberType);
589 blockSet |= complexType->prohibitedSubstitutions();
590 }
591
592 foundSubstitutionGroupTypeInheritance(headType, memberType: memberType->wxsSuperType(), derivationSet, blockSet);
593}
594
595bool XsdSchemaHelper::substitutionGroupOkTransitive(const XsdElement::Ptr &head, const XsdElement::Ptr &member, const NamePool::Ptr &namePool)
596{
597 // @see http://www.w3.org/TR/xmlschema11-1/#cos-equiv-derived-ok-rec
598
599 // 1
600 if ((member->name(namePool) == head->name(namePool)) && (member->type() == head->type()))
601 return true;
602
603 // 2.1
604 if (head->disallowedSubstitutions() & NamedSchemaComponent::SubstitutionConstraint)
605 return false;
606
607 // 2.2
608 {
609 QSet<XsdElement::Ptr> visitedElements;
610 if (!foundSubstitutionGroupTransitive(head, member, visitedElements))
611 return false;
612 }
613
614 // 2.3
615 {
616 QSet<SchemaType::DerivationMethod> derivationSet;
617 NamedSchemaComponent::BlockingConstraints blockSet;
618
619 foundSubstitutionGroupTypeInheritance(headType: head->type(), memberType: member->type(), derivationSet, blockSet);
620
621 NamedSchemaComponent::BlockingConstraints checkSet(blockSet);
622 checkSet |= head->disallowedSubstitutions();
623 if (head->type()->isComplexType() && head->type()->isDefinedBySchema()) {
624 const XsdComplexType::Ptr complexType(head->type());
625 checkSet |= complexType->prohibitedSubstitutions();
626 }
627
628 if ((checkSet & NamedSchemaComponent::RestrictionConstraint) && derivationSet.contains(value: SchemaType::DerivationRestriction))
629 return false;
630 if ((checkSet & NamedSchemaComponent::ExtensionConstraint) && derivationSet.contains(value: SchemaType::DerivationExtension))
631 return false;
632 if (checkSet & NamedSchemaComponent::SubstitutionConstraint)
633 return false;
634 }
635
636 return true;
637}
638
639bool XsdSchemaHelper::isValidAttributeGroupRestriction(const XsdAttributeGroup::Ptr &derivedAttributeGroup, const XsdAttributeGroup::Ptr &attributeGroup, const XsdSchemaContext::Ptr &context, QString &errorMsg)
640{
641 // @see http://www.w3.org/TR/xmlschema-1/#derivation-ok-restriction
642
643 const XsdAttributeUse::List derivedAttributeUses = derivedAttributeGroup->attributeUses();
644 const XsdAttributeUse::List baseAttributeUses = attributeGroup->attributeUses();
645
646 return isValidAttributeUsesRestriction(derivedAttributeUses, attributeUses: baseAttributeUses,
647 derivedWildcard: derivedAttributeGroup->wildcard(), wildcard: attributeGroup->wildcard(), context, errorMsg);
648}
649
650bool XsdSchemaHelper::isValidAttributeUsesRestriction(const XsdAttributeUse::List &derivedAttributeUses, const XsdAttributeUse::List &baseAttributeUses,
651 const XsdWildcard::Ptr &derivedWildcard, const XsdWildcard::Ptr &wildcard, const XsdSchemaContext::Ptr &context, QString &errorMsg)
652{
653 const NamePool::Ptr namePool(context->namePool());
654
655 QHash<QXmlName, XsdAttributeUse::Ptr> baseAttributeUsesLookup;
656 for (int i = 0; i < baseAttributeUses.count(); ++i)
657 baseAttributeUsesLookup.insert(akey: baseAttributeUses.at(i)->attribute()->name(namePool), avalue: baseAttributeUses.at(i));
658
659 QHash<QXmlName, XsdAttributeUse::Ptr> derivedAttributeUsesLookup;
660 for (int i = 0; i < derivedAttributeUses.count(); ++i)
661 derivedAttributeUsesLookup.insert(akey: derivedAttributeUses.at(i)->attribute()->name(namePool), avalue: derivedAttributeUses.at(i));
662
663 // 2
664 for (int i = 0; i < derivedAttributeUses.count(); ++i) {
665 const XsdAttributeUse::Ptr derivedAttributeUse = derivedAttributeUses.at(i);
666
667 // prohibited attributes are no real attributes, so skip them in that test here
668 if (derivedAttributeUse->useType() == XsdAttributeUse::ProhibitedUse)
669 continue;
670
671 if (baseAttributeUsesLookup.contains(akey: derivedAttributeUse->attribute()->name(namePool))) {
672 const XsdAttributeUse::Ptr baseAttributeUse(baseAttributeUsesLookup.value(akey: derivedAttributeUse->attribute()->name(namePool)));
673
674 // 2.1.1
675 if (baseAttributeUse->isRequired() == true && derivedAttributeUse->isRequired() == false) {
676 errorMsg = QtXmlPatterns::tr(sourceText: "Base attribute %1 is required but derived attribute is not.").arg(a: formatAttribute(attribute: baseAttributeUse->attribute()->displayName(namePool)));
677 return false;
678 }
679
680 // 2.1.2
681 if (!isSimpleDerivationOk(derivedType: derivedAttributeUse->attribute()->type(), baseType: baseAttributeUse->attribute()->type(), constraints: SchemaType::DerivationConstraints())) {
682 errorMsg = QtXmlPatterns::tr(sourceText: "Type of derived attribute %1 cannot be validly derived from type of base attribute.").arg(a: formatAttribute(attribute: derivedAttributeUse->attribute()->displayName(namePool)));
683 return false;
684 }
685
686 // 2.1.3
687 XsdAttributeUse::ValueConstraint::Ptr derivedConstraint;
688 if (derivedAttributeUse->valueConstraint())
689 derivedConstraint = derivedAttributeUse->valueConstraint();
690 else if (derivedAttributeUse->attribute()->valueConstraint())
691 derivedConstraint = XsdAttributeUse::ValueConstraint::fromAttributeValueConstraint(constraint: derivedAttributeUse->attribute()->valueConstraint());
692
693 XsdAttributeUse::ValueConstraint::Ptr baseConstraint;
694 if (baseAttributeUse->valueConstraint())
695 baseConstraint = baseAttributeUse->valueConstraint();
696 else if (baseAttributeUse->attribute()->valueConstraint())
697 baseConstraint = XsdAttributeUse::ValueConstraint::fromAttributeValueConstraint(constraint: baseAttributeUse->attribute()->valueConstraint());
698
699 bool ok = false;
700 if (!baseConstraint || baseConstraint->variety() == XsdAttributeUse::ValueConstraint::Default)
701 ok = true;
702
703 if (derivedConstraint && baseConstraint) {
704 const XsdTypeChecker checker(context, QVector<QXmlName>(), QSourceLocation(QUrl(QLatin1String("http://dummy.org")), 1, 1));
705 if (derivedConstraint->variety() == XsdAttributeUse::ValueConstraint::Fixed && checker.valuesAreEqual(value: derivedConstraint->value(), otherValue: baseConstraint->value(), type: baseAttributeUse->attribute()->type()))
706 ok = true;
707 }
708
709 if (!ok) {
710 errorMsg = QtXmlPatterns::tr(sourceText: "Value constraint of derived attribute %1 does not match value constraint of base attribute.").arg(a: formatAttribute(attribute: derivedAttributeUse->attribute()->displayName(namePool)));
711 return false;
712 }
713 } else {
714 if (!wildcard) {
715 errorMsg = QtXmlPatterns::tr(sourceText: "Derived attribute %1 does not exist in the base definition.").arg(a: formatAttribute(attribute: derivedAttributeUse->attribute()->displayName(namePool)));
716 return false;
717 }
718
719 QXmlName name = derivedAttributeUse->attribute()->name(namePool);
720
721 // wildcards using XsdWildcard::absentNamespace, so we have to fix that here
722 if (name.namespaceURI() == StandardNamespaces::empty)
723 name.setNamespaceURI(namePool->allocateNamespace(uri: XsdWildcard::absentNamespace()));
724
725 if (!wildcardAllowsExpandedName(name, wildcard, namePool)) {
726 errorMsg = QtXmlPatterns::tr(sourceText: "Derived attribute %1 does not match the wildcard in the base definition.").arg(a: formatAttribute(attribute: derivedAttributeUse->attribute()->displayName(namePool)));
727 return false;
728 }
729 }
730 }
731
732 // 3
733 for (int i = 0; i < baseAttributeUses.count(); ++i) {
734 const XsdAttributeUse::Ptr baseAttributeUse = baseAttributeUses.at(i);
735
736 if (baseAttributeUse->isRequired()) {
737 if (derivedAttributeUsesLookup.contains(akey: baseAttributeUse->attribute()->name(namePool))) {
738 if (!derivedAttributeUsesLookup.value(akey: baseAttributeUse->attribute()->name(namePool))->isRequired()) {
739 errorMsg = QtXmlPatterns::tr(sourceText: "Base attribute %1 is required but derived attribute is not.").arg(a: formatAttribute(attribute: baseAttributeUse->attribute()->displayName(namePool)));
740 return false;
741 }
742 } else {
743 errorMsg = QtXmlPatterns::tr(sourceText: "Base attribute %1 is required but missing in derived definition.").arg(a: formatAttribute(attribute: baseAttributeUse->attribute()->displayName(namePool)));
744 return false;
745 }
746 }
747 }
748
749 // 4
750 if (derivedWildcard) {
751 if (!wildcard) {
752 errorMsg = QtXmlPatterns::tr(sourceText: "Derived definition contains an %1 element that does not exists in the base definition").arg(a: formatElement(element: "anyAttribute."));
753 return false;
754 }
755
756 if (!isWildcardSubset(wildcard: derivedWildcard, otherWildcard: wildcard)) {
757 errorMsg = QtXmlPatterns::tr(sourceText: "Derived wildcard is not a subset of the base wildcard.");
758 return false;
759 }
760
761 if (!checkWildcardProcessContents(baseWildcard: wildcard, derivedWildcard)) {
762 errorMsg = QtXmlPatterns::tr(sourceText: "%1 of derived wildcard is not a valid restriction of %2 of base wildcard").arg(a: formatKeyword(keyword: "processContents")).arg(a: formatKeyword(keyword: "processContents."));
763 return false;
764 }
765 }
766
767 return true;
768}
769
770bool XsdSchemaHelper::isValidAttributeUsesExtension(const XsdAttributeUse::List &derivedAttributeUses, const XsdAttributeUse::List &attributeUses,
771 const XsdWildcard::Ptr &derivedWildcard, const XsdWildcard::Ptr &wildcard, const XsdSchemaContext::Ptr &context, QString &errorMsg)
772{
773 // @see http://www.w3.org/TR/xmlschema11-1/#cos-ct-extends
774
775 const NamePool::Ptr namePool(context->namePool());
776
777 // 1.2
778 QHash<QXmlName, XsdAttribute::Ptr> lookupHash;
779 for (int i = 0; i < derivedAttributeUses.count(); ++i)
780 lookupHash.insert(akey: derivedAttributeUses.at(i)->attribute()->name(namePool), avalue: derivedAttributeUses.at(i)->attribute());
781
782 for (int i = 0; i < attributeUses.count(); ++i) {
783 const QXmlName attributeName = attributeUses.at(i)->attribute()->name(namePool);
784 if (!lookupHash.contains(akey: attributeName)) {
785 errorMsg = QtXmlPatterns::tr(sourceText: "Attribute %1 from base type is missing in derived type.").arg(a: formatKeyword(keyword: namePool->displayName(qName: attributeName)));
786 return false;
787 }
788
789 if (lookupHash.value(akey: attributeName)->type() != attributeUses.at(i)->attribute()->type()) {
790 errorMsg = QtXmlPatterns::tr(sourceText: "Type of derived attribute %1 differs from type of base attribute.").arg(a: formatKeyword(keyword: namePool->displayName(qName: attributeName)));
791 return false;
792 }
793 }
794
795 // 1.3
796 if (wildcard) {
797 if (!derivedWildcard) {
798 errorMsg = QtXmlPatterns::tr(sourceText: "Base definition contains an %1 element that is missing in the derived definition").arg(a: formatElement(element: "anyAttribute."));
799 return false;
800 }
801 }
802
803 return true;
804}
805
806QT_END_NAMESPACE
807

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