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 
49 QT_BEGIN_NAMESPACE
50 
51 using 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  */
effectiveTotalRangeMinimum(const XsdParticle::Ptr & particle)57 static 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((unsigned int)minValue, particle->minimumOccurs());
78                 }
79             } else if (particle->term()->isModelGroup()) {
80                 if (minValue == -1) {
81                     minValue = effectiveTotalRangeMinimum(particle);
82                 } else {
83                     minValue = qMin((unsigned int)minValue, 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 
isParticleEmptiable(const XsdParticle::Ptr & particle)108 bool 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 
wildcardAllowsNamespaceName(const QString & nameSpace,const XsdWildcard::NamespaceConstraint::Ptr & constraint)121 bool 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(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(nameSpace))
139             return true;
140     }
141 
142     return false;
143 }
144 
wildcardAllowsExpandedName(const QXmlName & name,const XsdWildcard::Ptr & wildcard,const NamePool::Ptr & namePool)145 bool 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(namePool->stringForNamespace(name.namespaceURI()), wildcard->namespaceConstraint()))
151         return false;
152 
153     // 2, 3, 4
154     //TODO: we have no disallowed namespace yet
155 
156     return true;
157 }
158 
isWildcardSubset(const XsdWildcard::Ptr & wildcard,const XsdWildcard::Ptr & otherWildcard)159 bool 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(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(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(otherConstraint->namespaces()))
187             return true;
188     }
189 
190     return false;
191 }
192 
wildcardUnion(const XsdWildcard::Ptr & wildcard,const XsdWildcard::Ptr & otherWildcard)193 XsdWildcard::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(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(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(negatedSet.values().first()) && sSet.contains(XsdWildcard::absentNamespace())) { // 5.1
251             unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
252             return unionWildcard;
253         }
254         if (sSet.contains(negatedSet.values().first()) && !sSet.contains(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(negatedSet.values().first()) && sSet.contains(XsdWildcard::absentNamespace())) { // 5.3
260             return XsdWildcard::Ptr(); // not expressible
261         }
262         if (!sSet.contains(negatedSet.values().first()) && !sSet.contains(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(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(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(XsdWildcard::absentNamespace())) { // 6.1
287             unionWildcard->namespaceConstraint()->setVariety(XsdWildcard::NamespaceConstraint::Any);
288             return unionWildcard;
289         }
290         if (!sSet.contains(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 
wildcardIntersection(const XsdWildcard::Ptr & wildcard,const XsdWildcard::Ptr & otherWildcard)300 XsdWildcard::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(constraint->namespaces());
339         set.remove(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(otherConstraint->namespaces());
353         set.remove(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(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(XsdWildcard::absentNamespace())) && otherConstraint->namespaces().contains(XsdWildcard::absentNamespace())) {
378             return wildcard;
379         }
380         if (constraint->namespaces().contains(XsdWildcard::absentNamespace()) && !(otherConstraint->namespaces().contains(XsdWildcard::absentNamespace()))) {
381             return otherWildcard;
382         }
383     }
384 
385     // 5 as not expressible return empty wildcard
386     return XsdWildcard::Ptr();
387 }
388 
convertBlockingConstraints(const NamedSchemaComponent::BlockingConstraints & constraints)389 static 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 
isValidlySubstitutable(const SchemaType::Ptr & type,const SchemaType::Ptr & otherType,const SchemaType::DerivationConstraints & constraints)401 bool 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(XsdComplexType::Ptr(otherType)->prohibitedSubstitutions());
410 
411         return isComplexDerivationOk(type, otherType, keywords);
412     }
413 
414     // 2
415     if (type->isComplexType() && otherType->isSimpleType()) {
416         return isComplexDerivationOk(type, otherType, constraints);
417     }
418 
419     // 3
420     if (type->isSimpleType() && otherType->isSimpleType()) {
421         return isSimpleDerivationOk(type, otherType, constraints);
422     }
423 
424     return false;
425 }
426 
isSimpleDerivationOk(const SchemaType::Ptr & derivedType,const SchemaType::Ptr & baseType,const SchemaType::DerivationConstraints & constraints)427 bool 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->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, 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 
isComplexDerivationOk(const SchemaType::Ptr & derivedType,const SchemaType::Ptr & baseType,const SchemaType::DerivationConstraints & constraints)471 bool 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->wxsSuperType(), baseType, constraints);
504         } else { // 2.3.2.2
505             isOk = isSimpleDerivationOk(derivedType->wxsSuperType(), baseType, constraints);
506         }
507     }
508     if (isOk)
509         return true;
510 
511     return false;
512 }
513 
constructAndCompare(const DerivedString<TypeString>::Ptr & operand1,const AtomicComparator::Operator op,const DerivedString<TypeString>::Ptr & operand2,const SchemaType::Ptr & type,const ReportContext::Ptr & context,const SourceLocationReflection * const sourceLocationReflection)514 bool 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(context->namePool()) == BuiltinTypes::xsQName->name(context->namePool()))
526         return false;
527 
528     const AtomicValue::Ptr value1 = ValueFactory::fromLexical(operand1->stringValue(), type, context, sourceLocationReflection);
529     if (value1->hasError())
530         return false;
531 
532     const AtomicValue::Ptr value2 = ValueFactory::fromLexical(operand2->stringValue(), type, context, sourceLocationReflection);
533     if (value2->hasError())
534         return false;
535 
536     return ComparisonFactory::compare(value1, op, value2, type, context, sourceLocationReflection);
537 }
538 
checkWildcardProcessContents(const XsdWildcard::Ptr & baseWildcard,const XsdWildcard::Ptr & derivedWildcard)539 bool 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 
foundSubstitutionGroupTransitive(const XsdElement::Ptr & head,const XsdElement::Ptr & member,QSet<XsdElement::Ptr> & visitedElements)553 bool XsdSchemaHelper::foundSubstitutionGroupTransitive(const XsdElement::Ptr &head, const XsdElement::Ptr &member, QSet<XsdElement::Ptr> &visitedElements)
554 {
555     if (visitedElements.contains(member))
556         return false;
557     else
558         visitedElements.insert(member);
559 
560     if (member->substitutionGroupAffiliations().isEmpty())
561         return false;
562 
563     if (member->substitutionGroupAffiliations().contains(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, affiliations.at(i), visitedElements))
569                 return true;
570         }
571 
572         return false;
573     }
574 }
575 
foundSubstitutionGroupTypeInheritance(const SchemaType::Ptr & headType,const SchemaType::Ptr & memberType,QSet<SchemaType::DerivationMethod> & derivationSet,NamedSchemaComponent::BlockingConstraints & blockSet)576 void 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(memberType->derivationMethod());
586 
587     if (memberType->isComplexType()) {
588         const XsdComplexType::Ptr complexType(memberType);
589         blockSet |= complexType->prohibitedSubstitutions();
590     }
591 
592     foundSubstitutionGroupTypeInheritance(headType, memberType->wxsSuperType(), derivationSet, blockSet);
593 }
594 
substitutionGroupOkTransitive(const XsdElement::Ptr & head,const XsdElement::Ptr & member,const NamePool::Ptr & namePool)595 bool 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(head->type(), 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(SchemaType::DerivationRestriction))
629             return false;
630         if ((checkSet & NamedSchemaComponent::ExtensionConstraint) && derivationSet.contains(SchemaType::DerivationExtension))
631             return false;
632         if (checkSet & NamedSchemaComponent::SubstitutionConstraint)
633             return false;
634     }
635 
636     return true;
637 }
638 
isValidAttributeGroupRestriction(const XsdAttributeGroup::Ptr & derivedAttributeGroup,const XsdAttributeGroup::Ptr & attributeGroup,const XsdSchemaContext::Ptr & context,QString & errorMsg)639 bool 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, baseAttributeUses,
647                                            derivedAttributeGroup->wildcard(), attributeGroup->wildcard(), context, errorMsg);
648 }
649 
isValidAttributeUsesRestriction(const XsdAttributeUse::List & derivedAttributeUses,const XsdAttributeUse::List & baseAttributeUses,const XsdWildcard::Ptr & derivedWildcard,const XsdWildcard::Ptr & wildcard,const XsdSchemaContext::Ptr & context,QString & errorMsg)650 bool 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(baseAttributeUses.at(i)->attribute()->name(namePool), baseAttributeUses.at(i));
658 
659     QHash<QXmlName, XsdAttributeUse::Ptr> derivedAttributeUsesLookup;
660     for (int i = 0; i < derivedAttributeUses.count(); ++i)
661         derivedAttributeUsesLookup.insert(derivedAttributeUses.at(i)->attribute()->name(namePool), 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(derivedAttributeUse->attribute()->name(namePool))) {
672             const XsdAttributeUse::Ptr baseAttributeUse(baseAttributeUsesLookup.value(derivedAttributeUse->attribute()->name(namePool)));
673 
674             // 2.1.1
675             if (baseAttributeUse->isRequired() == true && derivedAttributeUse->isRequired() == false) {
676                 errorMsg = QtXmlPatterns::tr("Base attribute %1 is required but derived attribute is not.").arg(formatAttribute(baseAttributeUse->attribute()->displayName(namePool)));
677                 return false;
678             }
679 
680             // 2.1.2
681             if (!isSimpleDerivationOk(derivedAttributeUse->attribute()->type(), baseAttributeUse->attribute()->type(), SchemaType::DerivationConstraints())) {
682                 errorMsg = QtXmlPatterns::tr("Type of derived attribute %1 cannot be validly derived from type of base attribute.").arg(formatAttribute(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(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(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(derivedConstraint->value(), baseConstraint->value(), baseAttributeUse->attribute()->type()))
706                     ok = true;
707             }
708 
709             if (!ok) {
710                 errorMsg = QtXmlPatterns::tr("Value constraint of derived attribute %1 does not match value constraint of base attribute.").arg(formatAttribute(derivedAttributeUse->attribute()->displayName(namePool)));
711                 return false;
712             }
713         } else {
714             if (!wildcard) {
715                 errorMsg = QtXmlPatterns::tr("Derived attribute %1 does not exist in the base definition.").arg(formatAttribute(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(XsdWildcard::absentNamespace()));
724 
725             if (!wildcardAllowsExpandedName(name, wildcard, namePool)) {
726                 errorMsg = QtXmlPatterns::tr("Derived attribute %1 does not match the wildcard in the base definition.").arg(formatAttribute(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(baseAttributeUse->attribute()->name(namePool))) {
738                 if (!derivedAttributeUsesLookup.value(baseAttributeUse->attribute()->name(namePool))->isRequired()) {
739                     errorMsg = QtXmlPatterns::tr("Base attribute %1 is required but derived attribute is not.").arg(formatAttribute(baseAttributeUse->attribute()->displayName(namePool)));
740                     return false;
741                 }
742             } else {
743                 errorMsg = QtXmlPatterns::tr("Base attribute %1 is required but missing in derived definition.").arg(formatAttribute(baseAttributeUse->attribute()->displayName(namePool)));
744                 return false;
745             }
746         }
747     }
748 
749     // 4
750     if (derivedWildcard) {
751         if (!wildcard) {
752             errorMsg = QtXmlPatterns::tr("Derived definition contains an %1 element that does not exists in the base definition").arg(formatElement("anyAttribute."));
753             return false;
754         }
755 
756         if (!isWildcardSubset(derivedWildcard, wildcard)) {
757             errorMsg = QtXmlPatterns::tr("Derived wildcard is not a subset of the base wildcard.");
758             return false;
759         }
760 
761         if (!checkWildcardProcessContents(wildcard, derivedWildcard)) {
762             errorMsg = QtXmlPatterns::tr("%1 of derived wildcard is not a valid restriction of %2 of base wildcard").arg(formatKeyword("processContents")).arg(formatKeyword("processContents."));
763             return false;
764         }
765     }
766 
767     return true;
768 }
769 
isValidAttributeUsesExtension(const XsdAttributeUse::List & derivedAttributeUses,const XsdAttributeUse::List & attributeUses,const XsdWildcard::Ptr & derivedWildcard,const XsdWildcard::Ptr & wildcard,const XsdSchemaContext::Ptr & context,QString & errorMsg)770 bool 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(derivedAttributeUses.at(i)->attribute()->name(namePool), 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(attributeName)) {
785             errorMsg = QtXmlPatterns::tr("Attribute %1 from base type is missing in derived type.").arg(formatKeyword(namePool->displayName(attributeName)));
786             return false;
787         }
788 
789         if (lookupHash.value(attributeName)->type() != attributeUses.at(i)->attribute()->type()) {
790             errorMsg = QtXmlPatterns::tr("Type of derived attribute %1 differs from type of base attribute.").arg(formatKeyword(namePool->displayName(attributeName)));
791             return false;
792         }
793     }
794 
795     // 1.3
796     if (wildcard) {
797         if (!derivedWildcard) {
798             errorMsg = QtXmlPatterns::tr("Base definition contains an %1 element that is missing in the derived definition").arg(formatElement("anyAttribute."));
799             return false;
800         }
801     }
802 
803     return true;
804 }
805 
806 QT_END_NAMESPACE
807