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 | */ |
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(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 | |
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 | |
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(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 | |
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(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 | |
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(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 | |
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(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 | |
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(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 | |
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 | |
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(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 | |
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: 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 | |
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: 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 | |
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(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 | |
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 | |
553 | bool 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 | |
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(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 | |
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(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 | |
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, attributeUses: baseAttributeUses, |
647 | derivedWildcard: derivedAttributeGroup->wildcard(), wildcard: attributeGroup->wildcard(), context, errorMsg); |
648 | } |
649 | |
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(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 | |
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(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 | |
806 | QT_END_NAMESPACE |
807 | |