2
* schematron.c : implementation of the Schematron schema validity checking
4
* See Copyright for the status of this software.
6
* Daniel Veillard <daniel@veillard.com>
11
* + double check the semantic, especially
12
* - multiple rules applying in a single pattern/node
13
* - the semantic of libxml2 patterns vs. XSLT production referenced
15
* + export of results in SVRL
16
* + full parsing and coverage of the spec, conformance of the input to the
18
* + divergences between the draft and the ISO proposed standard :-(
19
* + hook and test include
20
* + try and compare with the XSLT version
26
#ifdef LIBXML_SCHEMATRON_ENABLED
29
#include <libxml/parser.h>
30
#include <libxml/tree.h>
31
#include <libxml/uri.h>
32
#include <libxml/xpath.h>
33
#include <libxml/xpathInternals.h>
34
#include <libxml/pattern.h>
35
#include <libxml/schematron.h>
37
#define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
39
#define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
41
#define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
44
static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
45
static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
47
#define IS_SCHEMATRON(node, elem) \
48
((node != NULL) && (node->type == XML_ELEMENT_NODE ) && \
49
(node->ns != NULL) && \
50
(xmlStrEqual(node->name, (const xmlChar *) elem)) && \
51
((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
52
(xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
54
#define NEXT_SCHEMATRON(node) \
55
while (node != NULL) { \
56
if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \
57
((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
58
(xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) \
66
* macro to flag unimplemented blocks
69
xmlGenericError(xmlGenericErrorContext, \
70
"Unimplemented block at %s:%d\n", \
74
XML_SCHEMATRON_ASSERT=1,
75
XML_SCHEMATRON_REPORT=2
76
} xmlSchematronTestType;
81
* A Schematrons test, either an assert or a report
83
typedef struct _xmlSchematronTest xmlSchematronTest;
84
typedef xmlSchematronTest *xmlSchematronTestPtr;
85
struct _xmlSchematronTest {
86
xmlSchematronTestPtr next; /* the next test in the list */
87
xmlSchematronTestType type; /* the test type */
88
xmlNodePtr node; /* the node in the tree */
89
xmlChar *test; /* the expression to test */
90
xmlXPathCompExprPtr comp; /* the compiled expression */
91
xmlChar *report; /* the message to report */
99
typedef struct _xmlSchematronRule xmlSchematronRule;
100
typedef xmlSchematronRule *xmlSchematronRulePtr;
101
struct _xmlSchematronRule {
102
xmlSchematronRulePtr next; /* the next rule in the list */
103
xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
104
xmlNodePtr node; /* the node in the tree */
105
xmlChar *context; /* the context evaluation rule */
106
xmlSchematronTestPtr tests; /* the list of tests */
107
xmlPatternPtr pattern; /* the compiled pattern associated */
108
xmlChar *report; /* the message to report */
112
* _xmlSchematronPattern:
114
* A Schematrons pattern
116
typedef struct _xmlSchematronPattern xmlSchematronPattern;
117
typedef xmlSchematronPattern *xmlSchematronPatternPtr;
118
struct _xmlSchematronPattern {
119
xmlSchematronPatternPtr next;/* the next pattern in the list */
120
xmlSchematronRulePtr rules; /* the list of rules */
121
xmlChar *name; /* the name of the pattern */
127
* A Schematrons definition
129
struct _xmlSchematron {
130
const xmlChar *name; /* schema name */
131
int preserve; /* was the document passed by the user */
132
xmlDocPtr doc; /* pointer to the parsed document */
133
int flags; /* specific to this schematron */
135
void *_private; /* unused by the library */
136
xmlDictPtr dict; /* the dictionnary used internally */
138
const xmlChar *title; /* the title if any */
140
int nbNs; /* the number of namespaces */
142
int nbPattern; /* the number of patterns */
143
xmlSchematronPatternPtr patterns;/* the patterns found */
144
xmlSchematronRulePtr rules; /* the rules gathered */
145
int nbNamespaces; /* number of namespaces in the array */
146
int maxNamespaces; /* size of the array */
147
const xmlChar **namespaces; /* the array of namespaces */
151
* xmlSchematronValidCtxt:
153
* A Schematrons validation context
155
struct _xmlSchematronValidCtxt {
157
int flags; /* an or of xmlSchematronValidOptions */
163
xmlSchematronPtr schema;
164
xmlXPathContextPtr xctxt;
166
FILE *outputFile; /* if using XML_SCHEMATRON_OUT_FILE */
167
xmlBufferPtr outputBuffer; /* if using XML_SCHEMATRON_OUT_BUFFER */
168
xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
169
xmlOutputCloseCallback ioclose;
173
struct _xmlSchematronParserCtxt {
177
int preserve; /* Whether the doc should be freed */
181
xmlDictPtr dict; /* dictionnary for interned string names */
185
xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
186
xmlSchematronPtr schema;
188
int nbNamespaces; /* number of namespaces in the array */
189
int maxNamespaces; /* size of the array */
190
const xmlChar **namespaces; /* the array of namespaces */
192
int nbIncludes; /* number of includes in the array */
193
int maxIncludes; /* size of the array */
194
xmlNodePtr *includes; /* the array of includes */
196
/* error rreporting data */
197
void *userData; /* user specific data block */
198
xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
199
xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
200
xmlStructuredErrorFunc serror; /* the structured function */
204
#define XML_STRON_CTXT_PARSER 1
205
#define XML_STRON_CTXT_VALIDATOR 2
207
/************************************************************************
211
************************************************************************/
214
* xmlSchematronPErrMemory:
215
* @node: a context node
216
* @extra: extra informations
218
* Handle an out of memory condition
221
xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
222
const char *extra, xmlNodePtr node)
226
__xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
232
* @ctxt: the parsing context
233
* @node: the context node
234
* @error: the error code
235
* @msg: the error message
239
* Handle a parser error
242
xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
243
const char *msg, const xmlChar * str1, const xmlChar * str2)
245
xmlGenericErrorFunc channel = NULL;
246
xmlStructuredErrorFunc schannel = NULL;
251
channel = ctxt->error;
252
data = ctxt->userData;
253
schannel = ctxt->serror;
255
__xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
256
error, XML_ERR_ERROR, NULL, 0,
257
(const char *) str1, (const char *) str2, NULL, 0, 0,
262
* xmlSchematronVTypeErrMemory:
263
* @node: a context node
264
* @extra: extra informations
266
* Handle an out of memory condition
269
xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
270
const char *extra, xmlNodePtr node)
274
ctxt->err = XML_SCHEMAV_INTERNAL;
276
__xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
280
/************************************************************************
282
* Parsing and compilation of the Schematrontrons *
284
************************************************************************/
287
* xmlSchematronAddTest:
288
* @ctxt: the schema parsing context
289
* @type: the type of test
290
* @rule: the parent rule
291
* @node: the node hosting the test
292
* @test: the associated test
293
* @report: the associated report string
295
* Add a test to a schematron
297
* Returns the new pointer or NULL in case of error
299
static xmlSchematronTestPtr
300
xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
301
xmlSchematronTestType type,
302
xmlSchematronRulePtr rule,
303
xmlNodePtr node, xmlChar *test, xmlChar *report)
305
xmlSchematronTestPtr ret;
306
xmlXPathCompExprPtr comp;
308
if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
313
* try first to compile the test expression
315
comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
317
xmlSchematronPErr(ctxt, node,
319
"Failed to compile test expression %s",
324
ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
326
xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
329
memset(ret, 0, sizeof(xmlSchematronTest));
334
ret->report = report;
336
if (rule->tests == NULL) {
339
xmlSchematronTestPtr prev = rule->tests;
341
while (prev->next != NULL)
349
* xmlSchematronFreeTests:
350
* @tests: a list of tests
352
* Free a list of tests.
355
xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
356
xmlSchematronTestPtr next;
358
while (tests != NULL) {
360
if (tests->test != NULL)
361
xmlFree(tests->test);
362
if (tests->comp != NULL)
363
xmlXPathFreeCompExpr(tests->comp);
364
if (tests->report != NULL)
365
xmlFree(tests->report);
372
* xmlSchematronAddRule:
373
* @ctxt: the schema parsing context
374
* @schema: a schema structure
375
* @node: the node hosting the rule
376
* @context: the associated context string
377
* @report: the associated report string
379
* Add a rule to a schematron
381
* Returns the new pointer or NULL in case of error
383
static xmlSchematronRulePtr
384
xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
385
xmlSchematronPatternPtr pat, xmlNodePtr node,
386
xmlChar *context, xmlChar *report)
388
xmlSchematronRulePtr ret;
389
xmlPatternPtr pattern;
391
if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
396
* Try first to compile the pattern
398
pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
400
if (pattern == NULL) {
401
xmlSchematronPErr(ctxt, node,
403
"Failed to compile context expression %s",
407
ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
409
xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
412
memset(ret, 0, sizeof(xmlSchematronRule));
414
ret->context = context;
415
ret->pattern = pattern;
416
ret->report = report;
418
if (schema->rules == NULL) {
421
xmlSchematronRulePtr prev = schema->rules;
423
while (prev->next != NULL)
428
if (pat->rules == NULL) {
431
xmlSchematronRulePtr prev = pat->rules;
433
while (prev->patnext != NULL)
434
prev = prev->patnext;
441
* xmlSchematronFreeRules:
442
* @rules: a list of rules
444
* Free a list of rules.
447
xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
448
xmlSchematronRulePtr next;
450
while (rules != NULL) {
453
xmlSchematronFreeTests(rules->tests);
454
if (rules->context != NULL)
455
xmlFree(rules->context);
457
xmlFreePattern(rules->pattern);
458
if (rules->report != NULL)
459
xmlFree(rules->report);
466
* xmlSchematronAddPattern:
467
* @ctxt: the schema parsing context
468
* @schema: a schema structure
469
* @node: the node hosting the pattern
470
* @id: the id or name of the pattern
472
* Add a pattern to a schematron
474
* Returns the new pointer or NULL in case of error
476
static xmlSchematronPatternPtr
477
xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
478
xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
480
xmlSchematronPatternPtr ret;
482
if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
485
ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
487
xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
490
memset(ret, 0, sizeof(xmlSchematronPattern));
493
if (schema->patterns == NULL) {
494
schema->patterns = ret;
496
xmlSchematronPatternPtr prev = schema->patterns;
498
while (prev->next != NULL)
506
* xmlSchematronFreePatterns:
507
* @patterns: a list of patterns
509
* Free a list of patterns.
512
xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
513
xmlSchematronPatternPtr next;
515
while (patterns != NULL) {
516
next = patterns->next;
517
if (patterns->name != NULL)
518
xmlFree(patterns->name);
525
* xmlSchematronNewSchematron:
526
* @ctxt: a schema validation context
528
* Allocate a new Schematron structure.
530
* Returns the newly allocated structure or NULL in case or error
532
static xmlSchematronPtr
533
xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
535
xmlSchematronPtr ret;
537
ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
539
xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
542
memset(ret, 0, sizeof(xmlSchematron));
543
ret->dict = ctxt->dict;
544
xmlDictReference(ret->dict);
551
* @schema: a schema structure
553
* Deallocate a Schematron structure.
556
xmlSchematronFree(xmlSchematronPtr schema)
561
if ((schema->doc != NULL) && (!(schema->preserve)))
562
xmlFreeDoc(schema->doc);
564
if (schema->namespaces != NULL)
565
xmlFree((char **) schema->namespaces);
567
xmlSchematronFreeRules(schema->rules);
568
xmlSchematronFreePatterns(schema->patterns);
569
xmlDictFree(schema->dict);
574
* xmlSchematronNewParserCtxt:
575
* @URL: the location of the schema
577
* Create an XML Schematrons parse context for that file/resource expected
578
* to contain an XML Schematrons file.
580
* Returns the parser context or NULL in case of error
582
xmlSchematronParserCtxtPtr
583
xmlSchematronNewParserCtxt(const char *URL)
585
xmlSchematronParserCtxtPtr ret;
591
(xmlSchematronParserCtxtPtr)
592
xmlMalloc(sizeof(xmlSchematronParserCtxt));
594
xmlSchematronPErrMemory(NULL, "allocating schema parser context",
598
memset(ret, 0, sizeof(xmlSchematronParserCtxt));
599
ret->type = XML_STRON_CTXT_PARSER;
600
ret->dict = xmlDictCreate();
601
ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
602
ret->includes = NULL;
603
ret->xctxt = xmlXPathNewContext(NULL);
604
if (ret->xctxt == NULL) {
605
xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
607
xmlSchematronFreeParserCtxt(ret);
610
ret->xctxt->flags = XML_XPATH_CHECKNS;
615
* xmlSchematronNewMemParserCtxt:
616
* @buffer: a pointer to a char array containing the schemas
617
* @size: the size of the array
619
* Create an XML Schematrons parse context for that memory buffer expected
620
* to contain an XML Schematrons file.
622
* Returns the parser context or NULL in case of error
624
xmlSchematronParserCtxtPtr
625
xmlSchematronNewMemParserCtxt(const char *buffer, int size)
627
xmlSchematronParserCtxtPtr ret;
629
if ((buffer == NULL) || (size <= 0))
633
(xmlSchematronParserCtxtPtr)
634
xmlMalloc(sizeof(xmlSchematronParserCtxt));
636
xmlSchematronPErrMemory(NULL, "allocating schema parser context",
640
memset(ret, 0, sizeof(xmlSchematronParserCtxt));
641
ret->buffer = buffer;
643
ret->dict = xmlDictCreate();
644
ret->xctxt = xmlXPathNewContext(NULL);
645
if (ret->xctxt == NULL) {
646
xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
648
xmlSchematronFreeParserCtxt(ret);
655
* xmlSchematronNewDocParserCtxt:
656
* @doc: a preparsed document tree
658
* Create an XML Schematrons parse context for that document.
659
* NB. The document may be modified during the parsing process.
661
* Returns the parser context or NULL in case of error
663
xmlSchematronParserCtxtPtr
664
xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
666
xmlSchematronParserCtxtPtr ret;
672
(xmlSchematronParserCtxtPtr)
673
xmlMalloc(sizeof(xmlSchematronParserCtxt));
675
xmlSchematronPErrMemory(NULL, "allocating schema parser context",
679
memset(ret, 0, sizeof(xmlSchematronParserCtxt));
681
ret->dict = xmlDictCreate();
682
/* The application has responsibility for the document */
684
ret->xctxt = xmlXPathNewContext(doc);
685
if (ret->xctxt == NULL) {
686
xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
688
xmlSchematronFreeParserCtxt(ret);
696
* xmlSchematronFreeParserCtxt:
697
* @ctxt: the schema parser context
699
* Free the resources associated to the schema parser context
702
xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
706
if (ctxt->doc != NULL && !ctxt->preserve)
707
xmlFreeDoc(ctxt->doc);
708
if (ctxt->xctxt != NULL) {
709
xmlXPathFreeContext(ctxt->xctxt);
711
if (ctxt->namespaces != NULL)
712
xmlFree((char **) ctxt->namespaces);
713
xmlDictFree(ctxt->dict);
718
* xmlSchematronPushInclude:
719
* @ctxt: the schema parser context
720
* @doc: the included document
721
* @cur: the current include node
723
* Add an included document
726
xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
727
xmlDocPtr doc, xmlNodePtr cur)
729
if (ctxt->includes == NULL) {
730
ctxt->maxIncludes = 10;
731
ctxt->includes = (xmlNodePtr *)
732
xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
733
if (ctxt->includes == NULL) {
734
xmlSchematronPErrMemory(NULL, "allocating parser includes",
738
ctxt->nbIncludes = 0;
739
} else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
743
xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
746
xmlSchematronPErrMemory(NULL, "allocating parser includes",
750
ctxt->includes = tmp;
751
ctxt->maxIncludes *= 2;
753
ctxt->includes[2 * ctxt->nbIncludes] = cur;
754
ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
759
* xmlSchematronPopInclude:
760
* @ctxt: the schema parser context
762
* Pop an include level. The included document is being freed
764
* Returns the node immediately following the include or NULL if the
765
* include list was empty.
768
xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
773
if (ctxt->nbIncludes <= 0)
776
doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
777
ret = ctxt->includes[2 * ctxt->nbIncludes];
782
return(xmlSchematronPopInclude(ctxt));
787
* xmlSchematronAddNamespace:
788
* @ctxt: the schema parser context
789
* @prefix: the namespace prefix
790
* @ns: the namespace name
792
* Add a namespace definition in the context
795
xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
796
const xmlChar *prefix, const xmlChar *ns)
798
if (ctxt->namespaces == NULL) {
799
ctxt->maxNamespaces = 10;
800
ctxt->namespaces = (const xmlChar **)
801
xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
802
if (ctxt->namespaces == NULL) {
803
xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
807
ctxt->nbNamespaces = 0;
808
} else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
811
tmp = (const xmlChar **)
812
xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
813
sizeof(const xmlChar *));
815
xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
819
ctxt->namespaces = tmp;
820
ctxt->maxNamespaces *= 2;
822
ctxt->namespaces[2 * ctxt->nbNamespaces] =
823
xmlDictLookup(ctxt->dict, ns, -1);
824
ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
825
xmlDictLookup(ctxt->dict, prefix, -1);
826
ctxt->nbNamespaces++;
827
ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
828
ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
833
* xmlSchematronParseRule:
834
* @ctxt: a schema validation context
835
* @rule: the rule node
837
* parse a rule element
840
xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
841
xmlSchematronPatternPtr pattern,
849
xmlSchematronRulePtr ruleptr;
850
xmlSchematronTestPtr testptr;
852
if ((ctxt == NULL) || (rule == NULL)) return;
854
context = xmlGetNoNsProp(rule, BAD_CAST "context");
855
if (context == NULL) {
856
xmlSchematronPErr(ctxt, rule,
858
"rule has no context attribute",
861
} else if (context[0] == 0) {
862
xmlSchematronPErr(ctxt, rule,
864
"rule has an empty context attribute",
869
ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
870
rule, context, NULL);
871
if (ruleptr == NULL) {
877
cur = rule->children;
878
NEXT_SCHEMATRON(cur);
879
while (cur != NULL) {
880
if (IS_SCHEMATRON(cur, "assert")) {
882
test = xmlGetNoNsProp(cur, BAD_CAST "test");
884
xmlSchematronPErr(ctxt, cur,
886
"assert has no test attribute",
888
} else if (test[0] == 0) {
889
xmlSchematronPErr(ctxt, cur,
891
"assert has an empty test attribute",
895
/* TODO will need dynamic processing instead */
896
report = xmlNodeGetContent(cur);
898
testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
899
ruleptr, cur, test, report);
903
} else if (IS_SCHEMATRON(cur, "report")) {
905
test = xmlGetNoNsProp(cur, BAD_CAST "test");
907
xmlSchematronPErr(ctxt, cur,
909
"assert has no test attribute",
911
} else if (test[0] == 0) {
912
xmlSchematronPErr(ctxt, cur,
914
"assert has an empty test attribute",
918
/* TODO will need dynamic processing instead */
919
report = xmlNodeGetContent(cur);
921
testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
922
ruleptr, cur, test, report);
927
xmlSchematronPErr(ctxt, cur,
929
"Expecting an assert or a report element instead of %s",
933
NEXT_SCHEMATRON(cur);
936
xmlSchematronPErr(ctxt, rule,
938
"rule has no assert nor report element", NULL, NULL);
943
* xmlSchematronParsePattern:
944
* @ctxt: a schema validation context
945
* @pat: the pattern node
947
* parse a pattern element
950
xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
953
xmlSchematronPatternPtr pattern;
957
if ((ctxt == NULL) || (pat == NULL)) return;
959
id = xmlGetNoNsProp(pat, BAD_CAST "id");
961
id = xmlGetNoNsProp(pat, BAD_CAST "name");
963
pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
964
if (pattern == NULL) {
970
NEXT_SCHEMATRON(cur);
971
while (cur != NULL) {
972
if (IS_SCHEMATRON(cur, "rule")) {
973
xmlSchematronParseRule(ctxt, pattern, cur);
976
xmlSchematronPErr(ctxt, cur,
978
"Expecting a rule element instead of %s", cur->name, NULL);
981
NEXT_SCHEMATRON(cur);
984
xmlSchematronPErr(ctxt, pat,
986
"Pattern has no rule element", NULL, NULL);
991
* xmlSchematronLoadInclude:
992
* @ctxt: a schema validation context
993
* @cur: the include element
995
* Load the include document, Push the current pointer
997
* Returns the updated node pointer
1000
xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1002
xmlNodePtr ret = NULL;
1003
xmlDocPtr doc = NULL;
1004
xmlChar *href = NULL;
1005
xmlChar *base = NULL;
1006
xmlChar *URI = NULL;
1008
if ((ctxt == NULL) || (cur == NULL))
1011
href = xmlGetNoNsProp(cur, BAD_CAST "href");
1013
xmlSchematronPErr(ctxt, cur,
1015
"Include has no href attribute", NULL, NULL);
1019
/* do the URI base composition, load and find the root */
1020
base = xmlNodeGetBase(cur->doc, cur);
1021
URI = xmlBuildURI(href, base);
1022
doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
1024
xmlSchematronPErr(ctxt, cur,
1025
XML_SCHEMAP_FAILED_LOAD,
1026
"could not load include '%s'.\n",
1030
ret = xmlDocGetRootElement(doc);
1032
xmlSchematronPErr(ctxt, cur,
1033
XML_SCHEMAP_FAILED_LOAD,
1034
"could not find root from include '%s'.\n",
1039
/* Success, push the include for rollback on exit */
1040
xmlSchematronPushInclude(ctxt, doc, cur);
1056
* xmlSchematronParse:
1057
* @ctxt: a schema validation context
1059
* parse a schema definition resource and build an internal
1060
* XML Shema struture which can be used to validate instances.
1062
* Returns the internal XML Schematron structure built from the resource or
1063
* NULL in case of error
1066
xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1068
xmlSchematronPtr ret = NULL;
1070
xmlNodePtr root, cur;
1079
* First step is to parse the input document into an DOM/Infoset
1081
if (ctxt->URL != NULL) {
1082
doc = xmlReadFile((const char *) ctxt->URL, NULL,
1083
SCHEMATRON_PARSE_OPTIONS);
1085
xmlSchematronPErr(ctxt, NULL,
1086
XML_SCHEMAP_FAILED_LOAD,
1087
"xmlSchematronParse: could not load '%s'.\n",
1092
} else if (ctxt->buffer != NULL) {
1093
doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1094
SCHEMATRON_PARSE_OPTIONS);
1096
xmlSchematronPErr(ctxt, NULL,
1097
XML_SCHEMAP_FAILED_PARSE,
1098
"xmlSchematronParse: could not parse.\n",
1102
doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1103
ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
1105
} else if (ctxt->doc != NULL) {
1110
xmlSchematronPErr(ctxt, NULL,
1111
XML_SCHEMAP_NOTHING_TO_PARSE,
1112
"xmlSchematronParse: could not parse.\n",
1118
* Then extract the root and Schematron parse it
1120
root = xmlDocGetRootElement(doc);
1122
xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1124
"The schema has no document element.\n", NULL, NULL);
1131
if (!IS_SCHEMATRON(root, "schema")) {
1132
xmlSchematronPErr(ctxt, root,
1134
"The XML document '%s' is not a XML schematron document",
1138
ret = xmlSchematronNewSchematron(ctxt);
1144
* scan the schema elements
1146
cur = root->children;
1147
NEXT_SCHEMATRON(cur);
1148
if (IS_SCHEMATRON(cur, "title")) {
1149
xmlChar *title = xmlNodeGetContent(cur);
1150
if (title != NULL) {
1151
ret->title = xmlDictLookup(ret->dict, title, -1);
1155
NEXT_SCHEMATRON(cur);
1157
while (IS_SCHEMATRON(cur, "ns")) {
1158
xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1159
xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1160
if ((uri == NULL) || (uri[0] == 0)) {
1161
xmlSchematronPErr(ctxt, cur,
1163
"ns element has no uri", NULL, NULL);
1165
if ((prefix == NULL) || (prefix[0] == 0)) {
1166
xmlSchematronPErr(ctxt, cur,
1168
"ns element has no prefix", NULL, NULL);
1170
if ((prefix) && (uri)) {
1171
xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1172
xmlSchematronAddNamespace(ctxt, prefix, uri);
1180
NEXT_SCHEMATRON(cur);
1182
while (cur != NULL) {
1183
if (IS_SCHEMATRON(cur, "pattern")) {
1184
xmlSchematronParsePattern(ctxt, cur);
1187
xmlSchematronPErr(ctxt, cur,
1189
"Expecting a pattern element instead of %s", cur->name, NULL);
1192
NEXT_SCHEMATRON(cur);
1194
if (ret->nbPattern == 0) {
1195
xmlSchematronPErr(ctxt, root,
1197
"The schematron document '%s' has no pattern",
1201
/* the original document must be kept for reporting */
1210
if (ctxt->nberrors != 0) {
1211
xmlSchematronFree(ret);
1214
ret->namespaces = ctxt->namespaces;
1215
ret->nbNamespaces = ctxt->nbNamespaces;
1216
ctxt->namespaces = NULL;
1222
/************************************************************************
1224
* Schematrontron Reports handler *
1226
************************************************************************/
1229
xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1230
xmlNodePtr cur, const xmlChar *xpath) {
1231
xmlNodePtr node = NULL;
1232
xmlXPathObjectPtr ret;
1234
if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1237
ctxt->xctxt->doc = cur->doc;
1238
ctxt->xctxt->node = cur;
1239
ret = xmlXPathEval(xpath, ctxt->xctxt);
1243
if ((ret->type == XPATH_NODESET) &&
1244
(ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1245
node = ret->nodesetval->nodeTab[0];
1247
xmlXPathFreeObject(ret);
1252
* xmlSchematronReportOutput:
1253
* @ctxt: the validation context
1254
* @cur: the current node tested
1255
* @msg: the message output
1257
* Output part of the report to whatever channel the user selected
1260
xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1261
xmlNodePtr cur ATTRIBUTE_UNUSED,
1264
fprintf(stderr, "%s", msg);
1268
* xmlSchematronFormatReport:
1269
* @ctxt: the validation context
1270
* @test: the test node
1271
* @cur: the current node tested
1273
* Build the string being reported to the user.
1275
* Returns a report string or NULL in case of error. The string needs
1276
* to be deallocated by teh caller
1279
xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1280
xmlNodePtr test, xmlNodePtr cur) {
1281
xmlChar *ret = NULL;
1282
xmlNodePtr child, node;
1284
if ((test == NULL) || (cur == NULL))
1287
child = test->children;
1288
while (child != NULL) {
1289
if ((child->type == XML_TEXT_NODE) ||
1290
(child->type == XML_CDATA_SECTION_NODE))
1291
ret = xmlStrcat(ret, child->content);
1292
else if (IS_SCHEMATRON(child, "name")) {
1295
path = xmlGetNoNsProp(child, BAD_CAST "path");
1299
node = xmlSchematronGetNode(ctxt, cur, path);
1305
if ((node->ns == NULL) || (node->ns->prefix == NULL))
1306
ret = xmlStrcat(ret, node->name);
1308
ret = xmlStrcat(ret, node->ns->prefix);
1309
ret = xmlStrcat(ret, BAD_CAST ":");
1310
ret = xmlStrcat(ret, node->name);
1313
child = child->next;
1318
* remove superfluous \n
1321
int len = xmlStrlen(ret);
1326
if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1327
while ((c == ' ') || (c == '\n') ||
1328
(c == '\r') || (c == '\t')) {
1340
child = child->next;
1346
* xmlSchematronReportSuccess:
1347
* @ctxt: the validation context
1348
* @test: the compiled test
1349
* @cur: the current node tested
1350
* @success: boolean value for the result
1352
* called from the validation engine when an assert or report test have
1356
xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
1357
xmlSchematronTestPtr test, xmlNodePtr cur, int success) {
1358
if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1360
/* if quiet and not SVRL report only failures */
1361
if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1362
((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
1363
(test->type == XML_SCHEMATRON_REPORT))
1365
if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1371
const xmlChar *report = NULL;
1373
if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
1374
((test->type == XML_SCHEMATRON_ASSERT) & (success)))
1376
line = xmlGetLineNo(cur);
1377
path = xmlGetNodePath(cur);
1379
path = (xmlChar *) cur->name;
1381
if ((test->report != NULL) && (test->report[0] != 0))
1382
report = test->report;
1384
if (test->node != NULL)
1385
report = xmlSchematronFormatReport(ctxt, test->node, cur);
1386
if (report == NULL) {
1387
if (test->type == XML_SCHEMATRON_ASSERT) {
1388
snprintf(msg, 999, "%s line %ld: node failed assert\n",
1389
(const char *) path, line);
1391
snprintf(msg, 999, "%s line %ld: node failed report\n",
1392
(const char *) path, line);
1395
snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1396
line, (const char *) report);
1397
xmlFree((char *) report);
1399
xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1400
if ((path != NULL) && (path != (xmlChar *) cur->name))
1406
* xmlSchematronReportPattern:
1407
* @ctxt: the validation context
1408
* @pattern: the current pattern
1410
* called from the validation engine when starting to check a pattern
1413
xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1414
xmlSchematronPatternPtr pattern) {
1415
if ((ctxt == NULL) || (pattern == NULL))
1417
if (ctxt->flags & XML_SCHEMATRON_OUT_QUIET)
1419
if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1424
if (pattern->name == NULL)
1426
snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1427
xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1432
/************************************************************************
1434
* Validation against a Schematrontron *
1436
************************************************************************/
1439
* xmlSchematronNewValidCtxt:
1440
* @schema: a precompiled XML Schematrons
1441
* @options: a set of xmlSchematronValidOptions
1443
* Create an XML Schematrons validation context based on the given schema.
1445
* Returns the validation context or NULL in case of error
1447
xmlSchematronValidCtxtPtr
1448
xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1451
xmlSchematronValidCtxtPtr ret;
1453
ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
1455
xmlSchematronVErrMemory(NULL, "allocating validation context",
1459
memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1460
ret->type = XML_STRON_CTXT_VALIDATOR;
1461
ret->schema = schema;
1462
ret->xctxt = xmlXPathNewContext(NULL);
1463
ret->flags = options;
1464
if (ret->xctxt == NULL) {
1465
xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
1467
xmlSchematronFreeValidCtxt(ret);
1470
for (i = 0;i < schema->nbNamespaces;i++) {
1471
if ((schema->namespaces[2 * i] == NULL) ||
1472
(schema->namespaces[2 * i + 1] == NULL))
1474
xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1475
schema->namespaces[2 * i]);
1481
* xmlSchematronFreeValidCtxt:
1482
* @ctxt: the schema validation context
1484
* Free the resources associated to the schema validation context
1487
xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1491
if (ctxt->xctxt != NULL)
1492
xmlXPathFreeContext(ctxt->xctxt);
1493
if (ctxt->dict != NULL)
1494
xmlDictFree(ctxt->dict);
1499
xmlSchematronNextNode(xmlNodePtr cur) {
1500
if (cur->children != NULL) {
1502
* Do not descend on entities declarations
1504
if (cur->children->type != XML_ENTITY_DECL) {
1505
cur = cur->children;
1509
if (cur->type != XML_DTD_NODE)
1514
while (cur->next != NULL) {
1516
if ((cur->type != XML_ENTITY_DECL) &&
1517
(cur->type != XML_DTD_NODE))
1523
if (cur == NULL) break;
1524
if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1525
if (cur->next != NULL) {
1529
} while (cur != NULL);
1534
* xmlSchematronRunTest:
1535
* @ctxt: the schema validation context
1536
* @test: the current test
1537
* @instance: the document instace tree
1538
* @cur: the current node in the instance
1540
* Validate a rule against a tree instance at a given position
1542
* Returns 1 in case of success, 0 if error and -1 in case of internal error
1545
xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1546
xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur)
1548
xmlXPathObjectPtr ret;
1552
ctxt->xctxt->doc = instance;
1553
ctxt->xctxt->node = cur;
1554
ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1558
switch (ret->type) {
1559
case XPATH_XSLT_TREE:
1561
if ((ret->nodesetval == NULL) ||
1562
(ret->nodesetval->nodeNr == 0))
1566
failed = !ret->boolval;
1569
if ((xmlXPathIsNaN(ret->floatval)) ||
1570
(ret->floatval == 0.0))
1574
if ((ret->stringval == NULL) ||
1575
(ret->stringval[0] == 0))
1578
case XPATH_UNDEFINED:
1581
case XPATH_LOCATIONSET:
1586
xmlXPathFreeObject(ret);
1588
if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
1590
else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1593
xmlSchematronReportSuccess(ctxt, test, cur, !failed);
1599
* xmlSchematronValidateDoc:
1600
* @ctxt: the schema validation context
1601
* @instance: the document instace tree
1603
* Validate a tree instance against the schematron
1605
* Returns 0 in case of success, -1 in case of internal error
1606
* and an error count otherwise.
1609
xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1611
xmlNodePtr cur, root;
1612
xmlSchematronPatternPtr pattern;
1613
xmlSchematronRulePtr rule;
1614
xmlSchematronTestPtr test;
1616
if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1617
(ctxt->schema->rules == NULL) || (instance == NULL))
1620
root = xmlDocGetRootElement(instance);
1626
if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1627
(ctxt->flags == 0)) {
1629
* we are just trying to assert the validity of the document,
1630
* speed primes over the output, run in a single pass
1633
while (cur != NULL) {
1634
rule = ctxt->schema->rules;
1635
while (rule != NULL) {
1636
if (xmlPatternMatch(rule->pattern, cur) == 1) {
1638
while (test != NULL) {
1639
xmlSchematronRunTest(ctxt, test, instance, cur);
1646
cur = xmlSchematronNextNode(cur);
1650
* Process all contexts one at a time
1652
pattern = ctxt->schema->patterns;
1654
while (pattern != NULL) {
1655
xmlSchematronReportPattern(ctxt, pattern);
1658
* TODO convert the pattern rule to a direct XPath and
1659
* compute directly instead of using the pattern matching
1660
* over the full document...
1661
* Check the exact semantic
1664
while (cur != NULL) {
1665
rule = pattern->rules;
1666
while (rule != NULL) {
1667
if (xmlPatternMatch(rule->pattern, cur) == 1) {
1669
while (test != NULL) {
1670
xmlSchematronRunTest(ctxt, test, instance, cur);
1674
rule = rule->patnext;
1677
cur = xmlSchematronNextNode(cur);
1679
pattern = pattern->next;
1682
return(ctxt->nberrors);
1691
xmlSchematronParserCtxtPtr pctxt;
1692
xmlSchematronValidCtxtPtr vctxt;
1693
xmlSchematronPtr schema = NULL;
1695
pctxt = xmlSchematronNewParserCtxt("tst.sct");
1696
if (pctxt == NULL) {
1697
fprintf(stderr, "failed to build schematron parser\n");
1699
schema = xmlSchematronParse(pctxt);
1700
if (schema == NULL) {
1701
fprintf(stderr, "failed to compile schematron\n");
1703
xmlSchematronFreeParserCtxt(pctxt);
1705
instance = xmlReadFile("tst.sct", NULL,
1706
XML_PARSE_NOENT | XML_PARSE_NOCDATA);
1707
if (instance == NULL) {
1708
fprintf(stderr, "failed to parse instance\n");
1710
if ((schema != NULL) && (instance != NULL)) {
1711
vctxt = xmlSchematronNewValidCtxt(schema);
1712
if (vctxt == NULL) {
1713
fprintf(stderr, "failed to build schematron validator\n");
1715
ret = xmlSchematronValidateDoc(vctxt, instance);
1716
xmlSchematronFreeValidCtxt(vctxt);
1719
xmlSchematronFree(schema);
1720
xmlFreeDoc(instance);
1728
#define bottom_schematron
1729
#include "elfgcchack.h"
1730
#endif /* LIBXML_SCHEMATRON_ENABLED */