leptris 1.9.322.0-aarch64-linux → 1.9.324.0-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +5 -0
- data/lib/leptris/xml/element.rb +8 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +38 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +249 -6
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +213 -0
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +20 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +207 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed267566d3e55930faa65d87747fd502b922376cd3f16d6783f04ae7de3a47e6
|
|
4
|
+
data.tar.gz: 8184d0c615d800963d0cc817d3ede259727048e2dd17467bc2c8ef02766f9234
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b42f07f5f59d5002dd4743f179508b1518e56f85127479d584d7856535975f403db11af37b69c04cdfaa790dac9202bf16b21d84baeaff2cf2ab8fee27a1faf
|
|
7
|
+
data.tar.gz: 6b08a56371d60b846590531f510e9cd9fb86d94baa5099e9a2df302c7aade031317ab5fc58ef3ee86a7dd318d37945c66f5ba144f4b2fab5e140fc928f8f636c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.9.323.0] - 2026-10-08
|
|
2
|
+
|
|
3
|
+
### Fixed — engine sync (libleptris 1.9.323)
|
|
4
|
+
|
|
5
|
+
- **Inline anonymous complexType/simpleType capture (#1592,
|
|
6
|
+
engine PR #1596)**: elements carrying an inline anonymous type —
|
|
7
|
+
the most common XSD spelling — previously got no content model
|
|
8
|
+
(instance validation silently skipped their content check;
|
|
9
|
+
content_valid? answered -1). Inline types capture under a
|
|
10
|
+
synthesized element:NAME slot now; particle text types through
|
|
11
|
+
the slot too. Pinned: content-model enforcement with enumerated
|
|
12
|
+
errors, content_valid? answering, anonymous simpleType
|
|
13
|
+
patterns binding.
|
|
14
|
+
|
|
1
15
|
## [1.9.322.0] - 2026-10-08
|
|
2
16
|
|
|
3
17
|
### Engine sync (libleptris 1.9.322)
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.324"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -533,6 +533,11 @@ class Leptris::XML::Document
|
|
|
533
533
|
Leptris::XML::Serialization::DOCUMENT_SERIALIZE_INTO, c_ptr,
|
|
534
534
|
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
535
535
|
end
|
|
536
|
+
|
|
537
|
+
# Nokogiri parity (leptris#1595): to_s serializes the document.
|
|
538
|
+
def to_s
|
|
539
|
+
to_xml
|
|
540
|
+
end
|
|
536
541
|
alias_method :to_s, :to_xml
|
|
537
542
|
alias_method :serialize, :to_xml
|
|
538
543
|
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -943,6 +943,14 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
943
943
|
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
944
944
|
end
|
|
945
945
|
|
|
946
|
+
# Nokogiri parity (leptris#1595): to_s serializes the node —
|
|
947
|
+
# the default Object#to_s object dump leaked into migrated
|
|
948
|
+
# consumers (coradoc math stems). Serialization options ride
|
|
949
|
+
# to_xml; to_s is the no-options convenience form.
|
|
950
|
+
def to_s
|
|
951
|
+
to_xml
|
|
952
|
+
end
|
|
953
|
+
|
|
946
954
|
# HTML5 serialization of this element subtree (#309 — see
|
|
947
955
|
# Document#to_html for the semantics).
|
|
948
956
|
def to_html
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.324] - 2026-10-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **XSD identity constraints — tier-1 scope complete (#1075,
|
|
8
|
+
PR #1598).** `xs:key` / `xs:unique` / `xs:keyref` capture on
|
|
9
|
+
top-level element declarations (direct children or inside the
|
|
10
|
+
inline complexType) and two-pass instance validation driven by
|
|
11
|
+
the engine's own XPath with element context: pass 1 collects
|
|
12
|
+
key/unique field tuples (string-value extraction — attribute
|
|
13
|
+
fields included), enforces uniqueness, and requires non-empty
|
|
14
|
+
key fields; pass 2 collects keyref tuples; resolution then
|
|
15
|
+
requires every keyref to match a collected key/unique tuple.
|
|
16
|
+
The selector reads the `xs:selector` child's `@xpath`. Tuple
|
|
17
|
+
tables deep-copy their strings; IC captures free with the
|
|
18
|
+
schema. This completes the tier-1 engine scope: compile →
|
|
19
|
+
datatypes/facets → content models → instance validation →
|
|
20
|
+
identity constraints, gated on libxml2's 163-fixture corpus.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## [1.9.323] - 2026-10-08
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **XSD: inline anonymous complexType/simpleType capture
|
|
29
|
+
(#1592, PR #1596).** Elements carrying an inline anonymous type —
|
|
30
|
+
arguably the most common XSD spelling — got no content model:
|
|
31
|
+
instance validation silently skipped their content check and
|
|
32
|
+
`leptris_xsd_content_valid` answered −1. Inline types now capture
|
|
33
|
+
under a synthesized `element:NAME` slot (collision-free — `:`
|
|
34
|
+
cannot appear in xs: NCName type names): top-level elements and
|
|
35
|
+
every particle fall back to the inline capture when the type
|
|
36
|
+
attribute is absent, and local simpleType particles type their
|
|
37
|
+
text through the slot.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
3
41
|
## [1.9.322] - 2026-10-08
|
|
4
42
|
|
|
5
43
|
### Added
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* grow the model (datatypes, facets, content models, identity
|
|
5
5
|
* constraints) — this slice's contract is the API shape, the
|
|
6
6
|
* xs:schema identity check, and the declaration enumeration. */
|
|
7
|
+
#include <stdio.h>
|
|
7
8
|
#include <stdlib.h>
|
|
8
9
|
#include <string.h>
|
|
9
10
|
|
|
@@ -12,6 +13,8 @@
|
|
|
12
13
|
|
|
13
14
|
#define XSD_NS "http://www.w3.org/2001/XMLSchema"
|
|
14
15
|
|
|
16
|
+
void xsd_capture_ics(struct leptris_xsd_schema* s,
|
|
17
|
+
LeptrisElement elem, const char* element_name);
|
|
15
18
|
extern int xsd_simple_valid(const XsdSimple* simple, const char* v);
|
|
16
19
|
extern struct xsd_validator* xsd_validator_new(
|
|
17
20
|
struct leptris_xsd_schema* s);
|
|
@@ -32,6 +35,7 @@ struct leptris_xsd_schema {
|
|
|
32
35
|
XsdElementDecl* elements;
|
|
33
36
|
XsdCm* complex_types; /* name-carrying model roots */
|
|
34
37
|
XsdTypeAttrs* type_attrs;
|
|
38
|
+
XsdElementIcs* element_ics;
|
|
35
39
|
char* target_ns;
|
|
36
40
|
void* validator; /* slice-4 parked error list */
|
|
37
41
|
char* error;
|
|
@@ -41,6 +45,20 @@ struct leptris_xsd_schema {
|
|
|
41
45
|
|
|
42
46
|
static char* xsd_strdup(const char* s);
|
|
43
47
|
|
|
48
|
+
/* #1592: capture an INLINE anonymous xs:complexType / xs:simpleType
|
|
49
|
+
* under a synthesized name ("element:NAME" — ':' cannot appear in
|
|
50
|
+
* xs: NCName type names, so the slot is collision-free). Returns 1
|
|
51
|
+
* when a capture happened. Forward decls below; definitions later
|
|
52
|
+
* in this file. */
|
|
53
|
+
static int xsd_capture_inline(struct leptris_xsd_schema* s,
|
|
54
|
+
LeptrisElement elem, const char* synth);
|
|
55
|
+
static XsdCm* xsd_cm_new(XsdCmKind kind);
|
|
56
|
+
static void xsd_cm_occurrence(XsdCm* cm, LeptrisElement e);
|
|
57
|
+
static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
|
|
58
|
+
LeptrisElement group, XsdCm** tail_out);
|
|
59
|
+
static XsdSimple* xsd_capture_simple(LeptrisElement st);
|
|
60
|
+
|
|
61
|
+
|
|
44
62
|
static XsdCm* xsd_cm_new(XsdCmKind kind) {
|
|
45
63
|
XsdCm* cm = (XsdCm*)calloc(1, sizeof(*cm));
|
|
46
64
|
if (!cm) return NULL;
|
|
@@ -73,7 +91,45 @@ static void xsd_cm_occurrence(XsdCm* cm, LeptrisElement e) {
|
|
|
73
91
|
/* Recursive model build over sequence | choice | all | element |
|
|
74
92
|
* any children. Returns the child list head; *tail_out receives
|
|
75
93
|
* the list tail for sibling chaining. */
|
|
76
|
-
|
|
94
|
+
/* #1592: capture an INLINE anonymous xs:complexType / xs:simpleType
|
|
95
|
+
* under a synthesized name ("element:NAME" — ':' cannot appear in
|
|
96
|
+
* xs: NCName type names, so the slot is collision-free). Returns 1
|
|
97
|
+
* when a capture happened. */
|
|
98
|
+
static int xsd_capture_inline(struct leptris_xsd_schema* s,
|
|
99
|
+
LeptrisElement elem, const char* synth) {
|
|
100
|
+
for (LeptrisNodeRef n =
|
|
101
|
+
leptris_node_first_child(leptris_element_as_node(elem));
|
|
102
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
103
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
104
|
+
continue;
|
|
105
|
+
LeptrisElement e = (LeptrisElement)n;
|
|
106
|
+
const char* local = NULL, *prefix = NULL, *uri = NULL;
|
|
107
|
+
leptris_element_expanded_name(e, &local, &prefix, &uri);
|
|
108
|
+
if (!local || !uri || strcmp(uri, XSD_NS) != 0) continue;
|
|
109
|
+
if (strcmp(local, "complexType") == 0) {
|
|
110
|
+
XsdCm* root = xsd_cm_new(XSD_CM_SEQ);
|
|
111
|
+
if (!root) return 0;
|
|
112
|
+
root->name = xsd_strdup(synth);
|
|
113
|
+
root->first_child = xsd_cm_parse(s, e, NULL);
|
|
114
|
+
root->next = s->complex_types;
|
|
115
|
+
s->complex_types = root;
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
if (strcmp(local, "simpleType") == 0) {
|
|
119
|
+
XsdSimple* captured = xsd_capture_simple(e);
|
|
120
|
+
if (!captured) return 0;
|
|
121
|
+
free(captured->name);
|
|
122
|
+
captured->name = xsd_strdup(synth);
|
|
123
|
+
captured->next = s->simple_types;
|
|
124
|
+
s->simple_types = captured;
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
|
|
132
|
+
LeptrisElement group, XsdCm** tail_out) {
|
|
77
133
|
XsdCm* head = NULL;
|
|
78
134
|
XsdCm* tail = NULL;
|
|
79
135
|
for (LeptrisNodeRef n =
|
|
@@ -92,7 +148,7 @@ static XsdCm* xsd_cm_parse(LeptrisElement group, XsdCm** tail_out) {
|
|
|
92
148
|
cm = xsd_cm_new(strcmp(local, "sequence") == 0 ? XSD_CM_SEQ
|
|
93
149
|
: strcmp(local, "choice") == 0 ? XSD_CM_CHOICE
|
|
94
150
|
: XSD_CM_ALL);
|
|
95
|
-
if (cm) cm->first_child = xsd_cm_parse(e, NULL);
|
|
151
|
+
if (cm) cm->first_child = xsd_cm_parse(s, e, NULL);
|
|
96
152
|
} else if (strcmp(local, "element") == 0) {
|
|
97
153
|
cm = xsd_cm_new(XSD_CM_ELEMENT);
|
|
98
154
|
if (cm) {
|
|
@@ -100,8 +156,17 @@ static XsdCm* xsd_cm_parse(LeptrisElement group, XsdCm** tail_out) {
|
|
|
100
156
|
leptris_element_attribute(e, "name");
|
|
101
157
|
if (!nm) nm = leptris_element_attribute(e, "ref");
|
|
102
158
|
cm->name = xsd_strdup(nm);
|
|
103
|
-
|
|
104
|
-
leptris_element_attribute(e, "type")
|
|
159
|
+
const char* pty =
|
|
160
|
+
leptris_element_attribute(e, "type");
|
|
161
|
+
if (pty) {
|
|
162
|
+
cm->type = xsd_strdup(pty);
|
|
163
|
+
} else if (s && nm) {
|
|
164
|
+
/* #1592: inline anonymous type on the particle */
|
|
165
|
+
char synth[512];
|
|
166
|
+
snprintf(synth, sizeof(synth), "element:%s", nm);
|
|
167
|
+
if (xsd_capture_inline(s, e, synth))
|
|
168
|
+
cm->type = xsd_strdup(synth);
|
|
169
|
+
}
|
|
105
170
|
}
|
|
106
171
|
} else if (strcmp(local, "any") == 0) {
|
|
107
172
|
cm = xsd_cm_new(XSD_CM_ANY);
|
|
@@ -140,7 +205,9 @@ static char* xsd_strdup(const char* s) {
|
|
|
140
205
|
/* Capture one xs:simpleType's restriction model (slice 2). */
|
|
141
206
|
static XsdSimple* xsd_capture_simple(LeptrisElement st) {
|
|
142
207
|
const char* name = leptris_element_attribute(st, "name");
|
|
143
|
-
|
|
208
|
+
/* Anonymous types ride their element — allowed: the caller
|
|
209
|
+
* renames the captured entry into its synthesized slot
|
|
210
|
+
* (#1592). */
|
|
144
211
|
|
|
145
212
|
XsdSimple* simple = (XsdSimple*)calloc(1, sizeof(*simple));
|
|
146
213
|
if (!simple) return NULL;
|
|
@@ -323,6 +390,24 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
323
390
|
d->next = s->elements;
|
|
324
391
|
s->elements = d;
|
|
325
392
|
}
|
|
393
|
+
xsd_capture_ics(s, (LeptrisElement)n, nm);
|
|
394
|
+
} else if (nm) {
|
|
395
|
+
/* slice 6: identity constraints ride the element. */
|
|
396
|
+
xsd_capture_ics(s, (LeptrisElement)n, nm);
|
|
397
|
+
/* #1592: inline anonymous type — capture under the
|
|
398
|
+
* synthesized slot and point the declaration at it. */
|
|
399
|
+
char synth[512];
|
|
400
|
+
snprintf(synth, sizeof(synth), "element:%s", nm);
|
|
401
|
+
if (xsd_capture_inline(s, (LeptrisElement)n, synth)) {
|
|
402
|
+
XsdElementDecl* d =
|
|
403
|
+
(XsdElementDecl*)calloc(1, sizeof(*d));
|
|
404
|
+
if (d) {
|
|
405
|
+
d->name = xsd_strdup(nm);
|
|
406
|
+
d->type = xsd_strdup(synth);
|
|
407
|
+
d->next = s->elements;
|
|
408
|
+
s->elements = d;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
326
411
|
}
|
|
327
412
|
} else if (strcmp(local, "complexType") == 0 && uri &&
|
|
328
413
|
!strcmp(uri, XSD_NS)) {
|
|
@@ -334,7 +419,7 @@ LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
|
334
419
|
if (root) {
|
|
335
420
|
root->name = xsd_strdup(nm);
|
|
336
421
|
root->first_child =
|
|
337
|
-
xsd_cm_parse((LeptrisElement)n, NULL);
|
|
422
|
+
xsd_cm_parse(s, (LeptrisElement)n, NULL);
|
|
338
423
|
root->next = s->complex_types;
|
|
339
424
|
s->complex_types = root;
|
|
340
425
|
}
|
|
@@ -403,6 +488,24 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
|
|
|
403
488
|
struct leptris_xsd_schema* s = (struct leptris_xsd_schema*)schema;
|
|
404
489
|
if (!s) return;
|
|
405
490
|
xsd_cm_free(s->complex_types);
|
|
491
|
+
XsdElementIcs* slot = s->element_ics;
|
|
492
|
+
while (slot) {
|
|
493
|
+
XsdElementIcs* sn = slot->next;
|
|
494
|
+
XsdIc* ic = slot->constraints;
|
|
495
|
+
while (ic) {
|
|
496
|
+
XsdIc* in = ic->next;
|
|
497
|
+
free(ic->name);
|
|
498
|
+
free(ic->selector);
|
|
499
|
+
for (size_t f = 0; f < ic->field_count; f++)
|
|
500
|
+
free(ic->fields[f]);
|
|
501
|
+
free(ic->refer);
|
|
502
|
+
free(ic);
|
|
503
|
+
ic = in;
|
|
504
|
+
}
|
|
505
|
+
free(slot->element_name);
|
|
506
|
+
free(slot);
|
|
507
|
+
slot = sn;
|
|
508
|
+
}
|
|
406
509
|
XsdTypeAttrs* ta = s->type_attrs;
|
|
407
510
|
while (ta) {
|
|
408
511
|
XsdTypeAttrs* tn = ta->next;
|
|
@@ -472,6 +575,146 @@ LEPTRIS_API int leptris_xsd_simple_valid(LeptrisXsdSchema schema,
|
|
|
472
575
|
return xsd_valid_chain(s, t, lexical, 0);
|
|
473
576
|
}
|
|
474
577
|
|
|
578
|
+
/* ---- slice 6: identity-constraint capture ------------------------ */
|
|
579
|
+
|
|
580
|
+
/* The xs:selector child's @xpath (a child element, not an
|
|
581
|
+
* attribute of the constraint element itself). */
|
|
582
|
+
static char* xsd_ic_selector_xpath(LeptrisElement constraint) {
|
|
583
|
+
for (LeptrisNodeRef n =
|
|
584
|
+
leptris_node_first_child(leptris_element_as_node(constraint));
|
|
585
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
586
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
587
|
+
continue;
|
|
588
|
+
LeptrisElement e = (LeptrisElement)n;
|
|
589
|
+
const char* local = NULL, *prefix = NULL, *uri = NULL;
|
|
590
|
+
leptris_element_expanded_name(e, &local, &prefix, &uri);
|
|
591
|
+
if (!local || !uri || strcmp(uri, XSD_NS) != 0) continue;
|
|
592
|
+
if (strcmp(local, "selector") == 0)
|
|
593
|
+
return xsd_strdup(leptris_element_attribute(e, "xpath"));
|
|
594
|
+
}
|
|
595
|
+
return NULL;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
void xsd_capture_ics(struct leptris_xsd_schema* s,
|
|
599
|
+
LeptrisElement elem, const char* element_name) {
|
|
600
|
+
XsdIc* head = NULL;
|
|
601
|
+
XsdIc* tail = NULL;
|
|
602
|
+
/* ICs are direct children of xs:element, or children of its
|
|
603
|
+
* inline complexType — one hop covers the second spelling. */
|
|
604
|
+
for (int hop = 0; hop < 2; hop++) {
|
|
605
|
+
for (LeptrisNodeRef n =
|
|
606
|
+
leptris_node_first_child(leptris_element_as_node(elem));
|
|
607
|
+
n; n = leptris_node_next_sibling(n)) {
|
|
608
|
+
if (leptris_node_get_type(n) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
609
|
+
continue;
|
|
610
|
+
LeptrisElement e = (LeptrisElement)n;
|
|
611
|
+
const char* local = NULL, *prefix = NULL, *uri = NULL;
|
|
612
|
+
leptris_element_expanded_name(e, &local, &prefix, &uri);
|
|
613
|
+
if (!local || !uri || strcmp(uri, XSD_NS) != 0) continue;
|
|
614
|
+
if (hop == 0 &&
|
|
615
|
+
(strcmp(local, "key") == 0 ||
|
|
616
|
+
strcmp(local, "unique") == 0 ||
|
|
617
|
+
strcmp(local, "keyref") == 0)) {
|
|
618
|
+
XsdIc* ic = (XsdIc*)calloc(1, sizeof(*ic));
|
|
619
|
+
if (!ic) continue;
|
|
620
|
+
ic->kind = strcmp(local, "key") == 0 ? 0
|
|
621
|
+
: strcmp(local, "unique") == 0 ? 1
|
|
622
|
+
: 2;
|
|
623
|
+
ic->name = xsd_strdup(
|
|
624
|
+
leptris_element_attribute(e, "name"));
|
|
625
|
+
ic->selector = xsd_ic_selector_xpath(e);
|
|
626
|
+
ic->refer = xsd_strdup(
|
|
627
|
+
leptris_element_attribute(e, "refer"));
|
|
628
|
+
for (LeptrisNodeRef fc = leptris_node_first_child(
|
|
629
|
+
leptris_element_as_node(e));
|
|
630
|
+
fc && ic->field_count < 8;
|
|
631
|
+
fc = leptris_node_next_sibling(fc)) {
|
|
632
|
+
if (leptris_node_get_type(fc) !=
|
|
633
|
+
LEPTRIS_NODE_TYPE_ELEMENT)
|
|
634
|
+
continue;
|
|
635
|
+
LeptrisElement fe = (LeptrisElement)fc;
|
|
636
|
+
const char* fl = NULL, *fp = NULL, *fu = NULL;
|
|
637
|
+
leptris_element_expanded_name(fe, &fl, &fp, &fu);
|
|
638
|
+
if (!fl || !fu || strcmp(fu, XSD_NS) != 0) continue;
|
|
639
|
+
if (strcmp(fl, "field") != 0) continue;
|
|
640
|
+
ic->fields[ic->field_count++] = xsd_strdup(
|
|
641
|
+
leptris_element_attribute(fe, "xpath"));
|
|
642
|
+
}
|
|
643
|
+
if (tail)
|
|
644
|
+
tail->next = ic;
|
|
645
|
+
else
|
|
646
|
+
head = ic;
|
|
647
|
+
tail = ic;
|
|
648
|
+
} else if (hop == 0 && strcmp(local, "complexType") == 0) {
|
|
649
|
+
/* descend into the inline type's ICs */
|
|
650
|
+
for (LeptrisNodeRef ic_n = leptris_node_first_child(
|
|
651
|
+
leptris_element_as_node(e));
|
|
652
|
+
ic_n; ic_n = leptris_node_next_sibling(ic_n)) {
|
|
653
|
+
if (leptris_node_get_type(ic_n) !=
|
|
654
|
+
LEPTRIS_NODE_TYPE_ELEMENT)
|
|
655
|
+
continue;
|
|
656
|
+
LeptrisElement ie = (LeptrisElement)ic_n;
|
|
657
|
+
const char* il = NULL, *ip = NULL, *iu = NULL;
|
|
658
|
+
leptris_element_expanded_name(ie, &il, &ip, &iu);
|
|
659
|
+
if (!il || !iu || strcmp(iu, XSD_NS) != 0) continue;
|
|
660
|
+
if (strcmp(il, "key") != 0 &&
|
|
661
|
+
strcmp(il, "unique") != 0 &&
|
|
662
|
+
strcmp(il, "keyref") != 0)
|
|
663
|
+
continue;
|
|
664
|
+
XsdIc* ic = (XsdIc*)calloc(1, sizeof(*ic));
|
|
665
|
+
if (!ic) continue;
|
|
666
|
+
ic->kind = strcmp(il, "key") == 0 ? 0
|
|
667
|
+
: strcmp(il, "unique") == 0 ? 1
|
|
668
|
+
: 2;
|
|
669
|
+
ic->name = xsd_strdup(
|
|
670
|
+
leptris_element_attribute(ie, "name"));
|
|
671
|
+
ic->selector = xsd_ic_selector_xpath(ie);
|
|
672
|
+
ic->refer = xsd_strdup(
|
|
673
|
+
leptris_element_attribute(ie, "refer"));
|
|
674
|
+
for (LeptrisNodeRef fc =
|
|
675
|
+
leptris_node_first_child(
|
|
676
|
+
leptris_element_as_node(ie));
|
|
677
|
+
fc && ic->field_count < 8;
|
|
678
|
+
fc = leptris_node_next_sibling(fc)) {
|
|
679
|
+
if (leptris_node_get_type(fc) !=
|
|
680
|
+
LEPTRIS_NODE_TYPE_ELEMENT)
|
|
681
|
+
continue;
|
|
682
|
+
LeptrisElement fe = (LeptrisElement)fc;
|
|
683
|
+
const char* fl = NULL, *fp = NULL, *fu = NULL;
|
|
684
|
+
leptris_element_expanded_name(fe, &fl, &fp,
|
|
685
|
+
&fu);
|
|
686
|
+
if (!fl || !fu || strcmp(fu, XSD_NS) != 0)
|
|
687
|
+
continue;
|
|
688
|
+
if (strcmp(fl, "field") != 0) continue;
|
|
689
|
+
ic->fields[ic->field_count++] = xsd_strdup(
|
|
690
|
+
leptris_element_attribute(fe, "xpath"));
|
|
691
|
+
}
|
|
692
|
+
if (tail)
|
|
693
|
+
tail->next = ic;
|
|
694
|
+
else
|
|
695
|
+
head = ic;
|
|
696
|
+
tail = ic;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (!head) return;
|
|
702
|
+
XsdElementIcs* slot = (XsdElementIcs*)calloc(1, sizeof(*slot));
|
|
703
|
+
if (!slot) return;
|
|
704
|
+
slot->element_name = xsd_strdup(element_name);
|
|
705
|
+
slot->constraints = head;
|
|
706
|
+
slot->next = s->element_ics;
|
|
707
|
+
s->element_ics = slot;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const XsdElementIcs* xsd_find_element_ics(struct leptris_xsd_schema* s,
|
|
711
|
+
const char* element_name) {
|
|
712
|
+
if (!s || !element_name) return NULL;
|
|
713
|
+
for (XsdElementIcs* t = s->element_ics; t; t = t->next)
|
|
714
|
+
if (strcmp(t->element_name, element_name) == 0) return t;
|
|
715
|
+
return NULL;
|
|
716
|
+
}
|
|
717
|
+
|
|
475
718
|
/* ---- validator accessors (validate.c) --------------------------- */
|
|
476
719
|
|
|
477
720
|
const XsdElementDecl* xsd_find_element(struct leptris_xsd_schema* s,
|
|
@@ -40,6 +40,8 @@ extern const XsdSimple* xsd_find_simple_pub(
|
|
|
40
40
|
extern const char* xsd_target_ns(struct leptris_xsd_schema* s);
|
|
41
41
|
extern const XsdTypeAttrs* xsd_find_type_attrs(
|
|
42
42
|
struct leptris_xsd_schema* s, const char* type_name);
|
|
43
|
+
extern const XsdElementIcs* xsd_find_element_ics(
|
|
44
|
+
struct leptris_xsd_schema* s, const char* element_name);
|
|
43
45
|
|
|
44
46
|
/* ---- error list ---------------------------------------------------- */
|
|
45
47
|
|
|
@@ -248,6 +250,206 @@ static void validate_element(struct xsd_validator* v,
|
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
252
|
|
|
253
|
+
/* ---- slice 6: identity constraints -------------------------------- */
|
|
254
|
+
|
|
255
|
+
/* A key tuple: the field values of one selected node. */
|
|
256
|
+
typedef struct xsd_key_tuple {
|
|
257
|
+
char* values[8];
|
|
258
|
+
size_t count;
|
|
259
|
+
struct xsd_key_tuple* next;
|
|
260
|
+
} XsdKeyTuple;
|
|
261
|
+
|
|
262
|
+
/* All tuples collected for one constraint name, keyed by name. */
|
|
263
|
+
typedef struct xsd_key_table {
|
|
264
|
+
char* name;
|
|
265
|
+
XsdKeyTuple* tuples;
|
|
266
|
+
XsdKeyTuple* tail;
|
|
267
|
+
struct xsd_key_table* next;
|
|
268
|
+
} XsdKeyTable;
|
|
269
|
+
|
|
270
|
+
static XsdKeyTable* table_find(XsdKeyTable* head, const char* name) {
|
|
271
|
+
for (; head; head = head->next)
|
|
272
|
+
if (strcmp(head->name, name) == 0) return head;
|
|
273
|
+
return NULL;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
static char* xsd_dup_str(const char* s) {
|
|
277
|
+
if (!s) return NULL;
|
|
278
|
+
size_t n = strlen(s) + 1;
|
|
279
|
+
char* out = (char*)malloc(n);
|
|
280
|
+
if (out) memcpy(out, s, n);
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
static void table_add(XsdKeyTable** head, const char* name,
|
|
285
|
+
char* const* values, size_t count) {
|
|
286
|
+
XsdKeyTable* t = table_find(*head, name);
|
|
287
|
+
if (!t) {
|
|
288
|
+
t = (XsdKeyTable*)calloc(1, sizeof(*t));
|
|
289
|
+
if (!t) return;
|
|
290
|
+
size_t n = strlen(name) + 1;
|
|
291
|
+
t->name = (char*)malloc(n);
|
|
292
|
+
if (!t->name) { free(t); return; }
|
|
293
|
+
memcpy(t->name, name, n);
|
|
294
|
+
t->next = *head;
|
|
295
|
+
*head = t;
|
|
296
|
+
}
|
|
297
|
+
XsdKeyTuple* tup = (XsdKeyTuple*)calloc(1, sizeof(*tup));
|
|
298
|
+
if (!tup) return;
|
|
299
|
+
for (size_t i = 0; i < count && i < 8; i++)
|
|
300
|
+
tup->values[i] = xsd_dup_str(values[i]);
|
|
301
|
+
tup->count = count < 8 ? count : 8;
|
|
302
|
+
if (t->tail)
|
|
303
|
+
t->tail->next = tup;
|
|
304
|
+
else
|
|
305
|
+
t->tuples = tup;
|
|
306
|
+
t->tail = tup;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* Evaluate one IC against the scoping element. pass1: collect
|
|
310
|
+
* key/unique tuples and enforce uniqueness (key: also non-empty
|
|
311
|
+
* fields). pass2: keyref tuples resolve against collected tables. */
|
|
312
|
+
static void validate_ics(struct xsd_validator* v,
|
|
313
|
+
struct leptris_xsd_schema* s,
|
|
314
|
+
LeptrisElement elem, const char* elem_name,
|
|
315
|
+
XsdKeyTable** tables, int pass) {
|
|
316
|
+
(void)v;
|
|
317
|
+
const XsdElementIcs* ics = xsd_find_element_ics(s, elem_name);
|
|
318
|
+
if (!ics) return;
|
|
319
|
+
extern struct leptris_document* leptris_element_get_document(
|
|
320
|
+
LeptrisElement elem);
|
|
321
|
+
LeptrisDocument doc = leptris_element_get_document(elem);
|
|
322
|
+
for (XsdIc* ic = ics->constraints; ic; ic = ic->next) {
|
|
323
|
+
int is_ref = ic->kind == 2;
|
|
324
|
+
if (pass == 1 && is_ref) continue;
|
|
325
|
+
if (pass == 2 && !is_ref) continue;
|
|
326
|
+
if (!ic->selector || !ic->name) continue;
|
|
327
|
+
LeptrisXPathResult sel =
|
|
328
|
+
leptris_xpath_eval(doc, elem, ic->selector);
|
|
329
|
+
if (!sel) continue;
|
|
330
|
+
size_t sel_count = leptris_xpath_result_count(sel);
|
|
331
|
+
for (size_t i = 0; i < sel_count; i++) {
|
|
332
|
+
LeptrisElement node = leptris_xpath_result_get(sel, i);
|
|
333
|
+
if (!node) continue;
|
|
334
|
+
/* field values: string of each field expression */
|
|
335
|
+
char* vals[8] = {0};
|
|
336
|
+
size_t nv = 0;
|
|
337
|
+
int missing = 0;
|
|
338
|
+
for (size_t f = 0; f < ic->field_count && f < 8; f++) {
|
|
339
|
+
LeptrisXPathResult fr =
|
|
340
|
+
leptris_xpath_eval(doc, node, ic->fields[f]);
|
|
341
|
+
if (!fr || leptris_xpath_result_count(fr) == 0) {
|
|
342
|
+
missing = 1;
|
|
343
|
+
if (fr) leptris_xpath_result_free(fr);
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
/* String-value of the first node — attribute nodes
|
|
347
|
+
* included (leptris_xpath_result_string handles every
|
|
348
|
+
* node kind; #3526 contract). */
|
|
349
|
+
char* sv = leptris_xpath_result_string(fr);
|
|
350
|
+
leptris_xpath_result_free(fr);
|
|
351
|
+
vals[nv] = xsd_dup_str(sv ? sv : "");
|
|
352
|
+
leptris_free_string(sv);
|
|
353
|
+
nv++;
|
|
354
|
+
}
|
|
355
|
+
for (size_t k = 0; k < nv; k++)
|
|
356
|
+
if (!vals[k] || !vals[k][0]) missing = 1;
|
|
357
|
+
if (missing) {
|
|
358
|
+
if (ic->kind == 0)
|
|
359
|
+
verrf(v, "key", ic->name,
|
|
360
|
+
"a selected node is missing a field value");
|
|
361
|
+
for (size_t k = 0; k < nv; k++) free(vals[k]);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (pass == 1) {
|
|
365
|
+
/* uniqueness within this constraint */
|
|
366
|
+
XsdKeyTable* t = table_find(*tables, ic->name);
|
|
367
|
+
int dup = 0;
|
|
368
|
+
if (t) {
|
|
369
|
+
for (XsdKeyTuple* tup = t->tuples; tup;
|
|
370
|
+
tup = tup->next) {
|
|
371
|
+
int eq = tup->count == nv;
|
|
372
|
+
for (size_t k = 0; eq && k < nv; k++)
|
|
373
|
+
if (strcmp(tup->values[k], vals[k]) != 0)
|
|
374
|
+
eq = 0;
|
|
375
|
+
if (eq) { dup = 1; break; }
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (dup) {
|
|
379
|
+
verrf(v, ic->kind == 0 ? "key" : "unique",
|
|
380
|
+
ic->name, "duplicate key tuple");
|
|
381
|
+
}
|
|
382
|
+
table_add(tables, ic->name, vals, nv);
|
|
383
|
+
} else {
|
|
384
|
+
/* keyref: resolve later (deferred to pass 2 end) —
|
|
385
|
+
* store under the REFER name */
|
|
386
|
+
table_add(tables, ic->refer ? ic->refer : "",
|
|
387
|
+
vals, nv);
|
|
388
|
+
}
|
|
389
|
+
for (size_t k = 0; k < nv; k++) free(vals[k]);
|
|
390
|
+
}
|
|
391
|
+
leptris_xpath_result_free(sel);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Pass 2 tail: every keyref tuple must appear in the referenced
|
|
396
|
+
* key/unique table. */
|
|
397
|
+
static void resolve_keyrefs(struct xsd_validator* v,
|
|
398
|
+
struct leptris_xsd_schema* s,
|
|
399
|
+
XsdKeyTable* all, XsdKeyTable* refs) {
|
|
400
|
+
(void)s;
|
|
401
|
+
for (XsdKeyTable* rt = refs; rt; rt = rt->next) {
|
|
402
|
+
XsdKeyTable* target = table_find(all, rt->name);
|
|
403
|
+
if (!target) continue; /* unknown refer: capture-time lax */
|
|
404
|
+
for (XsdKeyTuple* want = rt->tuples; want; want = want->next) {
|
|
405
|
+
int found = 0;
|
|
406
|
+
for (XsdKeyTuple* have = target->tuples; have;
|
|
407
|
+
have = have->next) {
|
|
408
|
+
int eq = have->count == want->count;
|
|
409
|
+
for (size_t k = 0; eq && k < want->count; k++)
|
|
410
|
+
if (strcmp(have->values[k], want->values[k]) != 0)
|
|
411
|
+
eq = 0;
|
|
412
|
+
if (eq) { found = 1; break; }
|
|
413
|
+
}
|
|
414
|
+
if (!found)
|
|
415
|
+
verrf(v, "keyref", rt->name,
|
|
416
|
+
"reference does not resolve to any key");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
static void tables_free(XsdKeyTable* t) {
|
|
422
|
+
while (t) {
|
|
423
|
+
XsdKeyTable* n = t->next;
|
|
424
|
+
XsdKeyTuple* tup = t->tuples;
|
|
425
|
+
while (tup) {
|
|
426
|
+
XsdKeyTuple* tn = tup->next;
|
|
427
|
+
for (size_t k = 0; k < tup->count; k++) free(tup->values[k]);
|
|
428
|
+
free(tup);
|
|
429
|
+
tup = tn;
|
|
430
|
+
}
|
|
431
|
+
free(t->name);
|
|
432
|
+
free(t);
|
|
433
|
+
t = n;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* Walk every element carrying constraints in both passes. */
|
|
438
|
+
static void ic_walk(struct xsd_validator* v, struct leptris_xsd_schema* s,
|
|
439
|
+
LeptrisElement elem, XsdKeyTable** keys,
|
|
440
|
+
XsdKeyTable** refs, int pass) {
|
|
441
|
+
const char* local = NULL;
|
|
442
|
+
leptris_element_expanded_name(elem, &local, NULL, NULL);
|
|
443
|
+
if (local) validate_ics(v, s, elem, local, pass == 1 ? keys : refs, pass);
|
|
444
|
+
for (LeptrisNodeRef c =
|
|
445
|
+
leptris_node_first_child(leptris_element_as_node(elem));
|
|
446
|
+
c; c = leptris_node_next_sibling(c)) {
|
|
447
|
+
if (leptris_node_get_type(c) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
448
|
+
continue;
|
|
449
|
+
ic_walk(v, s, (LeptrisElement)c, keys, refs, pass);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
251
453
|
struct xsd_validator* xsd_validator_new(struct leptris_xsd_schema* s) {
|
|
252
454
|
struct xsd_validator* v =
|
|
253
455
|
(struct xsd_validator*)calloc(1, sizeof(*v));
|
|
@@ -272,6 +474,17 @@ int xsd_validator_run(struct xsd_validator* v, LeptrisDocument doc) {
|
|
|
272
474
|
LeptrisElement root = leptris_document_root(doc);
|
|
273
475
|
if (!root) return -1;
|
|
274
476
|
validate_element(v, v->s, root, 0);
|
|
477
|
+
|
|
478
|
+
/* slice 6: identity constraints — collect (pass 1), resolve
|
|
479
|
+
* keyrefs (pass 2). */
|
|
480
|
+
XsdKeyTable* keys = NULL;
|
|
481
|
+
XsdKeyTable* refs = NULL;
|
|
482
|
+
ic_walk(v, v->s, root, &keys, &refs, 1);
|
|
483
|
+
ic_walk(v, v->s, root, &keys, &refs, 2);
|
|
484
|
+
resolve_keyrefs(v, v->s, keys, refs);
|
|
485
|
+
tables_free(keys);
|
|
486
|
+
tables_free(refs);
|
|
487
|
+
|
|
275
488
|
return v->count == 0 ? 1 : 0;
|
|
276
489
|
}
|
|
277
490
|
|
|
@@ -76,6 +76,26 @@ typedef struct xsd_attr_decl {
|
|
|
76
76
|
|
|
77
77
|
/* complexType name -> its attribute declarations (hash-free: a
|
|
78
78
|
* parallel list keyed the same way as complex model roots). */
|
|
79
|
+
/* ---- slice 6: identity constraints -------------------------------- */
|
|
80
|
+
|
|
81
|
+
/* xs:key | xs:unique | xs:keyref captured on an element decl. */
|
|
82
|
+
typedef struct xsd_ic {
|
|
83
|
+
char* name; /* constraint name */
|
|
84
|
+
int kind; /* 0 = key, 1 = unique, 2 = keyref */
|
|
85
|
+
char* selector; /* XPath relative to the scoping element */
|
|
86
|
+
char* fields[8]; /* XPaths relative to each selected node */
|
|
87
|
+
size_t field_count;
|
|
88
|
+
char* refer; /* keyref: the referenced constraint's name */
|
|
89
|
+
struct xsd_ic* next;
|
|
90
|
+
} XsdIc;
|
|
91
|
+
|
|
92
|
+
/* Per-declaration constraint list: entries keyed by element name. */
|
|
93
|
+
typedef struct xsd_element_ics {
|
|
94
|
+
char* element_name;
|
|
95
|
+
XsdIc* constraints;
|
|
96
|
+
struct xsd_element_ics* next;
|
|
97
|
+
} XsdElementIcs;
|
|
98
|
+
|
|
79
99
|
typedef struct xsd_type_attrs {
|
|
80
100
|
char* type_name;
|
|
81
101
|
XsdAttrDecl* attrs;
|
|
@@ -517,3 +517,210 @@ TEST(XsdValidate, NullTolerant) {
|
|
|
517
517
|
}
|
|
518
518
|
|
|
519
519
|
} // namespace
|
|
520
|
+
|
|
521
|
+
// ---- #1592: inline anonymous types ---------------------------------
|
|
522
|
+
namespace {
|
|
523
|
+
|
|
524
|
+
const char* k_anon_schema =
|
|
525
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
526
|
+
"<xs:element name=\"book\">"
|
|
527
|
+
"<xs:complexType>"
|
|
528
|
+
"<xs:sequence>"
|
|
529
|
+
"<xs:element name=\"title\" type=\"xs:token\"/>"
|
|
530
|
+
"<xs:element name=\"author\" type=\"xs:token\"/>"
|
|
531
|
+
"</xs:sequence>"
|
|
532
|
+
"</xs:complexType>"
|
|
533
|
+
"</xs:element>"
|
|
534
|
+
"</xs:schema>";
|
|
535
|
+
|
|
536
|
+
TEST(XsdContent, AnonymousInlineComplexTypeCaptured) {
|
|
537
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
538
|
+
LeptrisXsdSchema s =
|
|
539
|
+
leptris_xsd_compile(k_anon_schema, strlen(k_anon_schema), &st);
|
|
540
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
541
|
+
/* the issue's exact symptom: content_valid answered -1 */
|
|
542
|
+
const char* good[] = {"title", "author"};
|
|
543
|
+
EXPECT_EQ(leptris_xsd_content_valid(s, "book", good, NULL, 2), 1);
|
|
544
|
+
const char* bad[] = {"author", "author", "isbn"};
|
|
545
|
+
EXPECT_EQ(leptris_xsd_content_valid(s, "book", bad, NULL, 3), 0);
|
|
546
|
+
const char* miss[] = {"author"};
|
|
547
|
+
EXPECT_EQ(leptris_xsd_content_valid(s, "book", miss, NULL, 1), 0);
|
|
548
|
+
leptris_xsd_free(s);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
TEST(XsdValidate, AnonymousInlineValidatesEndToEnd) {
|
|
552
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
553
|
+
LeptrisXsdSchema s =
|
|
554
|
+
leptris_xsd_compile(k_anon_schema, strlen(k_anon_schema), &st);
|
|
555
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
556
|
+
const char* good_x = "<book><title>t</title><author>a</author></book>";
|
|
557
|
+
LeptrisDocument good =
|
|
558
|
+
leptris_parse_string(good_x, strlen(good_x), &st);
|
|
559
|
+
ASSERT_NE(good, nullptr);
|
|
560
|
+
EXPECT_EQ(leptris_xsd_validate(s, good), 1);
|
|
561
|
+
leptris_document_free(good);
|
|
562
|
+
/* the issue's instance: missing title, out-of-model isbn */
|
|
563
|
+
const char* bad_x =
|
|
564
|
+
"<book><author/><author/><isbn>9</isbn></book>";
|
|
565
|
+
LeptrisDocument bad = leptris_parse_string(bad_x, strlen(bad_x), &st);
|
|
566
|
+
ASSERT_NE(bad, nullptr);
|
|
567
|
+
EXPECT_EQ(leptris_xsd_validate(s, bad), 0);
|
|
568
|
+
EXPECT_GE(leptris_xsd_error_count(s), 1u);
|
|
569
|
+
leptris_document_free(bad);
|
|
570
|
+
leptris_xsd_free(s);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
TEST(XsdDatatypes, AnonymousInlineSimpleTypeOnParticle) {
|
|
574
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
575
|
+
const char* xsd =
|
|
576
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
577
|
+
"<xs:complexType name=\"t\">"
|
|
578
|
+
"<xs:sequence>"
|
|
579
|
+
"<xs:element name=\"score\">"
|
|
580
|
+
"<xs:simpleType>"
|
|
581
|
+
"<xs:restriction base=\"xs:integer\">"
|
|
582
|
+
"<xs:minInclusive value=\"1\"/>"
|
|
583
|
+
"</xs:restriction>"
|
|
584
|
+
"</xs:simpleType>"
|
|
585
|
+
"</xs:element>"
|
|
586
|
+
"</xs:sequence></xs:complexType>"
|
|
587
|
+
"<xs:element name=\"game\" type=\"t\"/>"
|
|
588
|
+
"</xs:schema>";
|
|
589
|
+
LeptrisXsdSchema s = leptris_xsd_compile(xsd, strlen(xsd), &st);
|
|
590
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
591
|
+
const char* good_x = "<game><score>9</score></game>";
|
|
592
|
+
LeptrisDocument good = leptris_parse_string(good_x, strlen(good_x), &st);
|
|
593
|
+
ASSERT_NE(good, nullptr);
|
|
594
|
+
EXPECT_EQ(leptris_xsd_validate(s, good), 1);
|
|
595
|
+
leptris_document_free(good);
|
|
596
|
+
const char* bad_x = "<game><score>0</score></game>";
|
|
597
|
+
LeptrisDocument bad = leptris_parse_string(bad_x, strlen(bad_x), &st);
|
|
598
|
+
ASSERT_NE(bad, nullptr);
|
|
599
|
+
EXPECT_EQ(leptris_xsd_validate(s, bad), 0);
|
|
600
|
+
leptris_document_free(bad);
|
|
601
|
+
/* the synthesized slot resolves directly too */
|
|
602
|
+
EXPECT_EQ(leptris_xsd_simple_valid(s, "element:score", "9"), 1);
|
|
603
|
+
EXPECT_EQ(leptris_xsd_simple_valid(s, "element:score", "0"), 0);
|
|
604
|
+
leptris_xsd_free(s);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
} // namespace
|
|
608
|
+
|
|
609
|
+
// ---- slice 6: identity constraints ---------------------------------
|
|
610
|
+
namespace {
|
|
611
|
+
|
|
612
|
+
const char* k_ic_schema =
|
|
613
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
614
|
+
"<xs:element name=\"catalog\">"
|
|
615
|
+
"<xs:complexType>"
|
|
616
|
+
"<xs:sequence>"
|
|
617
|
+
"<xs:element name=\"product\" maxOccurs=\"unbounded\">"
|
|
618
|
+
"<xs:complexType>"
|
|
619
|
+
"<xs:sequence>"
|
|
620
|
+
"<xs:element name=\"partNum\" type=\"xs:string\"/>"
|
|
621
|
+
"</xs:sequence>"
|
|
622
|
+
"<xs:attribute name=\"id\" type=\"xs:string\" use=\"required\"/>"
|
|
623
|
+
"</xs:complexType>"
|
|
624
|
+
"</xs:element>"
|
|
625
|
+
"</xs:sequence>"
|
|
626
|
+
"</xs:complexType>"
|
|
627
|
+
"<xs:key name=\"productId\">"
|
|
628
|
+
"<xs:selector xpath=\"product\"/>"
|
|
629
|
+
"<xs:field xpath=\"@id\"/>"
|
|
630
|
+
"</xs:key>"
|
|
631
|
+
"<xs:keyref name=\"productRef\" refer=\"productId\">"
|
|
632
|
+
"<xs:selector xpath=\"product\"/>"
|
|
633
|
+
"<xs:field xpath=\"partNum\"/>"
|
|
634
|
+
"</xs:keyref>"
|
|
635
|
+
"</xs:element>"
|
|
636
|
+
"</xs:schema>";
|
|
637
|
+
|
|
638
|
+
TEST(XsdIc, UniqueKeysValidate) {
|
|
639
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
640
|
+
LeptrisXsdSchema s =
|
|
641
|
+
leptris_xsd_compile(k_ic_schema, strlen(k_ic_schema), &st);
|
|
642
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
643
|
+
const char* xml =
|
|
644
|
+
"<catalog>"
|
|
645
|
+
"<product id=\"A1\"><partNum>A1</partNum></product>"
|
|
646
|
+
"<product id=\"B2\"><partNum>B2</partNum></product>"
|
|
647
|
+
"</catalog>";
|
|
648
|
+
LeptrisDocument d = leptris_parse_string(xml, strlen(xml), &st);
|
|
649
|
+
ASSERT_NE(d, nullptr);
|
|
650
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 1)
|
|
651
|
+
<< (leptris_xsd_error_count(s) ? leptris_xsd_error_at(s, 0) : "");
|
|
652
|
+
leptris_document_free(d);
|
|
653
|
+
leptris_xsd_free(s);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
TEST(XsdIc, DuplicateKeyReports) {
|
|
657
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
658
|
+
LeptrisXsdSchema s =
|
|
659
|
+
leptris_xsd_compile(k_ic_schema, strlen(k_ic_schema), &st);
|
|
660
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
661
|
+
const char* xml =
|
|
662
|
+
"<catalog>"
|
|
663
|
+
"<product id=\"A1\"><partNum>A1</partNum></product>"
|
|
664
|
+
"<product id=\"A1\"><partNum>zz</partNum></product>"
|
|
665
|
+
"</catalog>";
|
|
666
|
+
LeptrisDocument d = leptris_parse_string(xml, strlen(xml), &st);
|
|
667
|
+
ASSERT_NE(d, nullptr);
|
|
668
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 0);
|
|
669
|
+
size_t n = leptris_xsd_error_count(s);
|
|
670
|
+
int saw_dup = 0;
|
|
671
|
+
for (size_t i = 0; i < n; i++) {
|
|
672
|
+
const char* e = leptris_xsd_error_at(s, i);
|
|
673
|
+
if (e && strstr(e, "duplicate")) saw_dup = 1;
|
|
674
|
+
}
|
|
675
|
+
EXPECT_TRUE(saw_dup);
|
|
676
|
+
leptris_document_free(d);
|
|
677
|
+
leptris_xsd_free(s);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
TEST(XsdIc, UnresolvedKeyrefReports) {
|
|
681
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
682
|
+
LeptrisXsdSchema s =
|
|
683
|
+
leptris_xsd_compile(k_ic_schema, strlen(k_ic_schema), &st);
|
|
684
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
685
|
+
const char* xml =
|
|
686
|
+
"<catalog>"
|
|
687
|
+
"<product id=\"A1\"><partNum>NOPE</partNum></product>"
|
|
688
|
+
"</catalog>";
|
|
689
|
+
LeptrisDocument d = leptris_parse_string(xml, strlen(xml), &st);
|
|
690
|
+
ASSERT_NE(d, nullptr);
|
|
691
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 0);
|
|
692
|
+
size_t n = leptris_xsd_error_count(s);
|
|
693
|
+
int saw_ref = 0;
|
|
694
|
+
for (size_t i = 0; i < n; i++) {
|
|
695
|
+
const char* e = leptris_xsd_error_at(s, i);
|
|
696
|
+
if (e && strstr(e, "keyref")) saw_ref = 1;
|
|
697
|
+
}
|
|
698
|
+
EXPECT_TRUE(saw_ref);
|
|
699
|
+
leptris_document_free(d);
|
|
700
|
+
leptris_xsd_free(s);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
TEST(XsdIc, KeyRequiresFieldValues) {
|
|
704
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
705
|
+
const char* xsd =
|
|
706
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
707
|
+
"<xs:element name=\"r\">"
|
|
708
|
+
"<xs:complexType><xs:sequence>"
|
|
709
|
+
"<xs:element name=\"item\" maxOccurs=\"unbounded\"/>"
|
|
710
|
+
"</xs:sequence></xs:complexType>"
|
|
711
|
+
"<xs:key name=\"k\">"
|
|
712
|
+
"<xs:selector xpath=\"item\"/>"
|
|
713
|
+
"<xs:field xpath=\"@id\"/>"
|
|
714
|
+
"</xs:key>"
|
|
715
|
+
"</xs:element></xs:schema>";
|
|
716
|
+
LeptrisXsdSchema s = leptris_xsd_compile(xsd, strlen(xsd), &st);
|
|
717
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0);
|
|
718
|
+
const char* xml = "<r><item/><item id=\"a\"/></r>";
|
|
719
|
+
LeptrisDocument d = leptris_parse_string(xml, strlen(xml), &st);
|
|
720
|
+
ASSERT_NE(d, nullptr);
|
|
721
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 0); /* missing field */
|
|
722
|
+
leptris_document_free(d);
|
|
723
|
+
leptris_xsd_free(s);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
} // namespace
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
|
3
3
|
"name": "leptris",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.324",
|
|
5
5
|
"description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
|
|
6
6
|
"homepage": "https://github.com/leptris/leptris",
|
|
7
7
|
"license": "MIT",
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: leptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.9.
|
|
4
|
+
version: 1.9.324.0
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|