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 dictionary 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 #ifdef LIBXML_OUTPUT_ENABLED
169 xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
170 xmlOutputCloseCallback ioclose;
174 /* error reporting data */
175 void *userData; /* user specific data block */
176 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
177 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
178 xmlStructuredErrorFunc serror; /* the structured function */
181 struct _xmlSchematronParserCtxt {
185 int preserve; /* Whether the doc should be freed */
189 xmlDictPtr dict; /* dictionary for interned string names */
193 xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
194 xmlSchematronPtr schema;
196 int nbNamespaces; /* number of namespaces in the array */
197 int maxNamespaces; /* size of the array */
198 const xmlChar **namespaces; /* the array of namespaces */
200 int nbIncludes; /* number of includes in the array */
201 int maxIncludes; /* size of the array */
202 xmlNodePtr *includes; /* the array of includes */
204 /* error reporting data */
205 void *userData; /* user specific data block */
206 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
207 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
208 xmlStructuredErrorFunc serror; /* the structured function */
211 #define XML_STRON_CTXT_PARSER 1
212 #define XML_STRON_CTXT_VALIDATOR 2
214 /************************************************************************
218 ************************************************************************/
221 * xmlSchematronPErrMemory:
222 * @node: a context node
223 * @extra: extra informations
225 * Handle an out of memory condition
228 xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
229 const char *extra, xmlNodePtr node)
233 __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
239 * @ctxt: the parsing context
240 * @node: the context node
241 * @error: the error code
242 * @msg: the error message
246 * Handle a parser error
248 static void LIBXML_ATTR_FORMAT(4,0)
249 xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
250 const char *msg, const xmlChar * str1, const xmlChar * str2)
252 xmlGenericErrorFunc channel = NULL;
253 xmlStructuredErrorFunc schannel = NULL;
258 channel = ctxt->error;
259 data = ctxt->userData;
260 schannel = ctxt->serror;
262 __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
263 error, XML_ERR_ERROR, NULL, 0,
264 (const char *) str1, (const char *) str2, NULL, 0, 0,
269 * xmlSchematronVTypeErrMemory:
270 * @node: a context node
271 * @extra: extra informations
273 * Handle an out of memory condition
276 xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
277 const char *extra, xmlNodePtr node)
281 ctxt->err = XML_SCHEMAV_INTERNAL;
283 __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
287 /************************************************************************
289 * Parsing and compilation of the Schematrontrons *
291 ************************************************************************/
294 * xmlSchematronAddTest:
295 * @ctxt: the schema parsing context
296 * @type: the type of test
297 * @rule: the parent rule
298 * @node: the node hosting the test
299 * @test: the associated test
300 * @report: the associated report string
302 * Add a test to a schematron
304 * Returns the new pointer or NULL in case of error
306 static xmlSchematronTestPtr
307 xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
308 xmlSchematronTestType type,
309 xmlSchematronRulePtr rule,
310 xmlNodePtr node, xmlChar *test, xmlChar *report)
312 xmlSchematronTestPtr ret;
313 xmlXPathCompExprPtr comp;
315 if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
320 * try first to compile the test expression
322 comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
324 xmlSchematronPErr(ctxt, node,
326 "Failed to compile test expression %s",
331 ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
333 xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
336 memset(ret, 0, sizeof(xmlSchematronTest));
341 ret->report = report;
343 if (rule->tests == NULL) {
346 xmlSchematronTestPtr prev = rule->tests;
348 while (prev->next != NULL)
356 * xmlSchematronFreeTests:
357 * @tests: a list of tests
359 * Free a list of tests.
362 xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
363 xmlSchematronTestPtr next;
365 while (tests != NULL) {
367 if (tests->test != NULL)
368 xmlFree(tests->test);
369 if (tests->comp != NULL)
370 xmlXPathFreeCompExpr(tests->comp);
371 if (tests->report != NULL)
372 xmlFree(tests->report);
379 * xmlSchematronAddRule:
380 * @ctxt: the schema parsing context
381 * @schema: a schema structure
382 * @node: the node hosting the rule
383 * @context: the associated context string
384 * @report: the associated report string
386 * Add a rule to a schematron
388 * Returns the new pointer or NULL in case of error
390 static xmlSchematronRulePtr
391 xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
392 xmlSchematronPatternPtr pat, xmlNodePtr node,
393 xmlChar *context, xmlChar *report)
395 xmlSchematronRulePtr ret;
396 xmlPatternPtr pattern;
398 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
403 * Try first to compile the pattern
405 pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
407 if (pattern == NULL) {
408 xmlSchematronPErr(ctxt, node,
410 "Failed to compile context expression %s",
414 ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
416 xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
419 memset(ret, 0, sizeof(xmlSchematronRule));
421 ret->context = context;
422 ret->pattern = pattern;
423 ret->report = report;
425 if (schema->rules == NULL) {
428 xmlSchematronRulePtr prev = schema->rules;
430 while (prev->next != NULL)
435 if (pat->rules == NULL) {
438 xmlSchematronRulePtr prev = pat->rules;
440 while (prev->patnext != NULL)
441 prev = prev->patnext;
448 * xmlSchematronFreeRules:
449 * @rules: a list of rules
451 * Free a list of rules.
454 xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
455 xmlSchematronRulePtr next;
457 while (rules != NULL) {
460 xmlSchematronFreeTests(rules->tests);
461 if (rules->context != NULL)
462 xmlFree(rules->context);
464 xmlFreePattern(rules->pattern);
465 if (rules->report != NULL)
466 xmlFree(rules->report);
473 * xmlSchematronAddPattern:
474 * @ctxt: the schema parsing context
475 * @schema: a schema structure
476 * @node: the node hosting the pattern
477 * @id: the id or name of the pattern
479 * Add a pattern to a schematron
481 * Returns the new pointer or NULL in case of error
483 static xmlSchematronPatternPtr
484 xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
485 xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
487 xmlSchematronPatternPtr ret;
489 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
492 ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
494 xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
497 memset(ret, 0, sizeof(xmlSchematronPattern));
500 if (schema->patterns == NULL) {
501 schema->patterns = ret;
503 xmlSchematronPatternPtr prev = schema->patterns;
505 while (prev->next != NULL)
513 * xmlSchematronFreePatterns:
514 * @patterns: a list of patterns
516 * Free a list of patterns.
519 xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
520 xmlSchematronPatternPtr next;
522 while (patterns != NULL) {
523 next = patterns->next;
524 if (patterns->name != NULL)
525 xmlFree(patterns->name);
532 * xmlSchematronNewSchematron:
533 * @ctxt: a schema validation context
535 * Allocate a new Schematron structure.
537 * Returns the newly allocated structure or NULL in case or error
539 static xmlSchematronPtr
540 xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
542 xmlSchematronPtr ret;
544 ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
546 xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
549 memset(ret, 0, sizeof(xmlSchematron));
550 ret->dict = ctxt->dict;
551 xmlDictReference(ret->dict);
558 * @schema: a schema structure
560 * Deallocate a Schematron structure.
563 xmlSchematronFree(xmlSchematronPtr schema)
568 if ((schema->doc != NULL) && (!(schema->preserve)))
569 xmlFreeDoc(schema->doc);
571 if (schema->namespaces != NULL)
572 xmlFree((char **) schema->namespaces);
574 xmlSchematronFreeRules(schema->rules);
575 xmlSchematronFreePatterns(schema->patterns);
576 xmlDictFree(schema->dict);
581 * xmlSchematronNewParserCtxt:
582 * @URL: the location of the schema
584 * Create an XML Schematrons parse context for that file/resource expected
585 * to contain an XML Schematrons file.
587 * Returns the parser context or NULL in case of error
589 xmlSchematronParserCtxtPtr
590 xmlSchematronNewParserCtxt(const char *URL)
592 xmlSchematronParserCtxtPtr ret;
598 (xmlSchematronParserCtxtPtr)
599 xmlMalloc(sizeof(xmlSchematronParserCtxt));
601 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
605 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
606 ret->type = XML_STRON_CTXT_PARSER;
607 ret->dict = xmlDictCreate();
608 ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
609 ret->includes = NULL;
610 ret->xctxt = xmlXPathNewContext(NULL);
611 if (ret->xctxt == NULL) {
612 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
614 xmlSchematronFreeParserCtxt(ret);
617 ret->xctxt->flags = XML_XPATH_CHECKNS;
622 * xmlSchematronNewMemParserCtxt:
623 * @buffer: a pointer to a char array containing the schemas
624 * @size: the size of the array
626 * Create an XML Schematrons parse context for that memory buffer expected
627 * to contain an XML Schematrons file.
629 * Returns the parser context or NULL in case of error
631 xmlSchematronParserCtxtPtr
632 xmlSchematronNewMemParserCtxt(const char *buffer, int size)
634 xmlSchematronParserCtxtPtr ret;
636 if ((buffer == NULL) || (size <= 0))
640 (xmlSchematronParserCtxtPtr)
641 xmlMalloc(sizeof(xmlSchematronParserCtxt));
643 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
647 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
648 ret->buffer = buffer;
650 ret->dict = xmlDictCreate();
651 ret->xctxt = xmlXPathNewContext(NULL);
652 if (ret->xctxt == NULL) {
653 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
655 xmlSchematronFreeParserCtxt(ret);
662 * xmlSchematronNewDocParserCtxt:
663 * @doc: a preparsed document tree
665 * Create an XML Schematrons parse context for that document.
666 * NB. The document may be modified during the parsing process.
668 * Returns the parser context or NULL in case of error
670 xmlSchematronParserCtxtPtr
671 xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
673 xmlSchematronParserCtxtPtr ret;
679 (xmlSchematronParserCtxtPtr)
680 xmlMalloc(sizeof(xmlSchematronParserCtxt));
682 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
686 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
688 ret->dict = xmlDictCreate();
689 /* The application has responsibility for the document */
691 ret->xctxt = xmlXPathNewContext(doc);
692 if (ret->xctxt == NULL) {
693 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
695 xmlSchematronFreeParserCtxt(ret);
703 * xmlSchematronFreeParserCtxt:
704 * @ctxt: the schema parser context
706 * Free the resources associated to the schema parser context
709 xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
713 if (ctxt->doc != NULL && !ctxt->preserve)
714 xmlFreeDoc(ctxt->doc);
715 if (ctxt->xctxt != NULL) {
716 xmlXPathFreeContext(ctxt->xctxt);
718 if (ctxt->namespaces != NULL)
719 xmlFree((char **) ctxt->namespaces);
720 xmlDictFree(ctxt->dict);
726 * xmlSchematronPushInclude:
727 * @ctxt: the schema parser context
728 * @doc: the included document
729 * @cur: the current include node
731 * Add an included document
734 xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
735 xmlDocPtr doc, xmlNodePtr cur)
737 if (ctxt->includes == NULL) {
738 ctxt->maxIncludes = 10;
739 ctxt->includes = (xmlNodePtr *)
740 xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
741 if (ctxt->includes == NULL) {
742 xmlSchematronPErrMemory(NULL, "allocating parser includes",
746 ctxt->nbIncludes = 0;
747 } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
751 xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
754 xmlSchematronPErrMemory(NULL, "allocating parser includes",
758 ctxt->includes = tmp;
759 ctxt->maxIncludes *= 2;
761 ctxt->includes[2 * ctxt->nbIncludes] = cur;
762 ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
767 * xmlSchematronPopInclude:
768 * @ctxt: the schema parser context
770 * Pop an include level. The included document is being freed
772 * Returns the node immediately following the include or NULL if the
773 * include list was empty.
776 xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
781 if (ctxt->nbIncludes <= 0)
784 doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
785 ret = ctxt->includes[2 * ctxt->nbIncludes];
790 return(xmlSchematronPopInclude(ctxt));
796 * xmlSchematronAddNamespace:
797 * @ctxt: the schema parser context
798 * @prefix: the namespace prefix
799 * @ns: the namespace name
801 * Add a namespace definition in the context
804 xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
805 const xmlChar *prefix, const xmlChar *ns)
807 if (ctxt->namespaces == NULL) {
808 ctxt->maxNamespaces = 10;
809 ctxt->namespaces = (const xmlChar **)
810 xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
811 if (ctxt->namespaces == NULL) {
812 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
816 ctxt->nbNamespaces = 0;
817 } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
820 tmp = (const xmlChar **)
821 xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
822 sizeof(const xmlChar *));
824 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
828 ctxt->namespaces = tmp;
829 ctxt->maxNamespaces *= 2;
831 ctxt->namespaces[2 * ctxt->nbNamespaces] =
832 xmlDictLookup(ctxt->dict, ns, -1);
833 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
834 xmlDictLookup(ctxt->dict, prefix, -1);
835 ctxt->nbNamespaces++;
836 ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
837 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
842 * xmlSchematronParseRule:
843 * @ctxt: a schema validation context
844 * @rule: the rule node
846 * parse a rule element
849 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
850 xmlSchematronPatternPtr pattern,
858 xmlSchematronRulePtr ruleptr;
859 xmlSchematronTestPtr testptr;
861 if ((ctxt == NULL) || (rule == NULL)) return;
863 context = xmlGetNoNsProp(rule, BAD_CAST "context");
864 if (context == NULL) {
865 xmlSchematronPErr(ctxt, rule,
867 "rule has no context attribute",
870 } else if (context[0] == 0) {
871 xmlSchematronPErr(ctxt, rule,
873 "rule has an empty context attribute",
878 ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
879 rule, context, NULL);
880 if (ruleptr == NULL) {
886 cur = rule->children;
887 NEXT_SCHEMATRON(cur);
888 while (cur != NULL) {
889 if (IS_SCHEMATRON(cur, "assert")) {
891 test = xmlGetNoNsProp(cur, BAD_CAST "test");
893 xmlSchematronPErr(ctxt, cur,
895 "assert has no test attribute",
897 } else if (test[0] == 0) {
898 xmlSchematronPErr(ctxt, cur,
900 "assert has an empty test attribute",
904 /* TODO will need dynamic processing instead */
905 report = xmlNodeGetContent(cur);
907 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
908 ruleptr, cur, test, report);
912 } else if (IS_SCHEMATRON(cur, "report")) {
914 test = xmlGetNoNsProp(cur, BAD_CAST "test");
916 xmlSchematronPErr(ctxt, cur,
918 "assert has no test attribute",
920 } else if (test[0] == 0) {
921 xmlSchematronPErr(ctxt, cur,
923 "assert has an empty test attribute",
927 /* TODO will need dynamic processing instead */
928 report = xmlNodeGetContent(cur);
930 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
931 ruleptr, cur, test, report);
936 xmlSchematronPErr(ctxt, cur,
938 "Expecting an assert or a report element instead of %s",
942 NEXT_SCHEMATRON(cur);
945 xmlSchematronPErr(ctxt, rule,
947 "rule has no assert nor report element", NULL, NULL);
952 * xmlSchematronParsePattern:
953 * @ctxt: a schema validation context
954 * @pat: the pattern node
956 * parse a pattern element
959 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
962 xmlSchematronPatternPtr pattern;
966 if ((ctxt == NULL) || (pat == NULL)) return;
968 id = xmlGetNoNsProp(pat, BAD_CAST "id");
970 id = xmlGetNoNsProp(pat, BAD_CAST "name");
972 pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
973 if (pattern == NULL) {
979 NEXT_SCHEMATRON(cur);
980 while (cur != NULL) {
981 if (IS_SCHEMATRON(cur, "rule")) {
982 xmlSchematronParseRule(ctxt, pattern, cur);
985 xmlSchematronPErr(ctxt, cur,
987 "Expecting a rule element instead of %s", cur->name, NULL);
990 NEXT_SCHEMATRON(cur);
993 xmlSchematronPErr(ctxt, pat,
995 "Pattern has no rule element", NULL, NULL);
1001 * xmlSchematronLoadInclude:
1002 * @ctxt: a schema validation context
1003 * @cur: the include element
1005 * Load the include document, Push the current pointer
1007 * Returns the updated node pointer
1010 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1012 xmlNodePtr ret = NULL;
1013 xmlDocPtr doc = NULL;
1014 xmlChar *href = NULL;
1015 xmlChar *base = NULL;
1016 xmlChar *URI = NULL;
1018 if ((ctxt == NULL) || (cur == NULL))
1021 href = xmlGetNoNsProp(cur, BAD_CAST "href");
1023 xmlSchematronPErr(ctxt, cur,
1025 "Include has no href attribute", NULL, NULL);
1029 /* do the URI base composition, load and find the root */
1030 base = xmlNodeGetBase(cur->doc, cur);
1031 URI = xmlBuildURI(href, base);
1032 doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
1034 xmlSchematronPErr(ctxt, cur,
1035 XML_SCHEMAP_FAILED_LOAD,
1036 "could not load include '%s'.\n",
1040 ret = xmlDocGetRootElement(doc);
1042 xmlSchematronPErr(ctxt, cur,
1043 XML_SCHEMAP_FAILED_LOAD,
1044 "could not find root from include '%s'.\n",
1049 /* Success, push the include for rollback on exit */
1050 xmlSchematronPushInclude(ctxt, doc, cur);
1067 * xmlSchematronParse:
1068 * @ctxt: a schema validation context
1070 * parse a schema definition resource and build an internal
1071 * XML Shema struture which can be used to validate instances.
1073 * Returns the internal XML Schematron structure built from the resource or
1074 * NULL in case of error
1077 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1079 xmlSchematronPtr ret = NULL;
1081 xmlNodePtr root, cur;
1090 * First step is to parse the input document into an DOM/Infoset
1092 if (ctxt->URL != NULL) {
1093 doc = xmlReadFile((const char *) ctxt->URL, NULL,
1094 SCHEMATRON_PARSE_OPTIONS);
1096 xmlSchematronPErr(ctxt, NULL,
1097 XML_SCHEMAP_FAILED_LOAD,
1098 "xmlSchematronParse: could not load '%s'.\n",
1103 } else if (ctxt->buffer != NULL) {
1104 doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1105 SCHEMATRON_PARSE_OPTIONS);
1107 xmlSchematronPErr(ctxt, NULL,
1108 XML_SCHEMAP_FAILED_PARSE,
1109 "xmlSchematronParse: could not parse.\n",
1113 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1114 ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
1116 } else if (ctxt->doc != NULL) {
1121 xmlSchematronPErr(ctxt, NULL,
1122 XML_SCHEMAP_NOTHING_TO_PARSE,
1123 "xmlSchematronParse: could not parse.\n",
1129 * Then extract the root and Schematron parse it
1131 root = xmlDocGetRootElement(doc);
1133 xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1135 "The schema has no document element.\n", NULL, NULL);
1142 if (!IS_SCHEMATRON(root, "schema")) {
1143 xmlSchematronPErr(ctxt, root,
1145 "The XML document '%s' is not a XML schematron document",
1149 ret = xmlSchematronNewSchematron(ctxt);
1155 * scan the schema elements
1157 cur = root->children;
1158 NEXT_SCHEMATRON(cur);
1159 if (IS_SCHEMATRON(cur, "title")) {
1160 xmlChar *title = xmlNodeGetContent(cur);
1161 if (title != NULL) {
1162 ret->title = xmlDictLookup(ret->dict, title, -1);
1166 NEXT_SCHEMATRON(cur);
1168 while (IS_SCHEMATRON(cur, "ns")) {
1169 xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1170 xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1171 if ((uri == NULL) || (uri[0] == 0)) {
1172 xmlSchematronPErr(ctxt, cur,
1174 "ns element has no uri", NULL, NULL);
1176 if ((prefix == NULL) || (prefix[0] == 0)) {
1177 xmlSchematronPErr(ctxt, cur,
1179 "ns element has no prefix", NULL, NULL);
1181 if ((prefix) && (uri)) {
1182 xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1183 xmlSchematronAddNamespace(ctxt, prefix, uri);
1191 NEXT_SCHEMATRON(cur);
1193 while (cur != NULL) {
1194 if (IS_SCHEMATRON(cur, "pattern")) {
1195 xmlSchematronParsePattern(ctxt, cur);
1198 xmlSchematronPErr(ctxt, cur,
1200 "Expecting a pattern element instead of %s", cur->name, NULL);
1203 NEXT_SCHEMATRON(cur);
1205 if (ret->nbPattern == 0) {
1206 xmlSchematronPErr(ctxt, root,
1208 "The schematron document '%s' has no pattern",
1212 /* the original document must be kept for reporting */
1224 if (ctxt->nberrors != 0) {
1225 xmlSchematronFree(ret);
1228 ret->namespaces = ctxt->namespaces;
1229 ret->nbNamespaces = ctxt->nbNamespaces;
1230 ctxt->namespaces = NULL;
1236 /************************************************************************
1238 * Schematrontron Reports handler *
1240 ************************************************************************/
1243 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1244 xmlNodePtr cur, const xmlChar *xpath) {
1245 xmlNodePtr node = NULL;
1246 xmlXPathObjectPtr ret;
1248 if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1251 ctxt->xctxt->doc = cur->doc;
1252 ctxt->xctxt->node = cur;
1253 ret = xmlXPathEval(xpath, ctxt->xctxt);
1257 if ((ret->type == XPATH_NODESET) &&
1258 (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1259 node = ret->nodesetval->nodeTab[0];
1261 xmlXPathFreeObject(ret);
1266 * xmlSchematronReportOutput:
1267 * @ctxt: the validation context
1268 * @cur: the current node tested
1269 * @msg: the message output
1271 * Output part of the report to whatever channel the user selected
1274 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1275 xmlNodePtr cur ATTRIBUTE_UNUSED,
1278 fprintf(stderr, "%s", msg);
1282 * xmlSchematronFormatReport:
1283 * @ctxt: the validation context
1284 * @test: the test node
1285 * @cur: the current node tested
1287 * Build the string being reported to the user.
1289 * Returns a report string or NULL in case of error. The string needs
1290 * to be deallocated by teh caller
1293 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1294 xmlNodePtr test, xmlNodePtr cur) {
1295 xmlChar *ret = NULL;
1296 xmlNodePtr child, node;
1298 if ((test == NULL) || (cur == NULL))
1301 child = test->children;
1302 while (child != NULL) {
1303 if ((child->type == XML_TEXT_NODE) ||
1304 (child->type == XML_CDATA_SECTION_NODE))
1305 ret = xmlStrcat(ret, child->content);
1306 else if (IS_SCHEMATRON(child, "name")) {
1309 path = xmlGetNoNsProp(child, BAD_CAST "path");
1313 node = xmlSchematronGetNode(ctxt, cur, path);
1319 if ((node->ns == NULL) || (node->ns->prefix == NULL))
1320 ret = xmlStrcat(ret, node->name);
1322 ret = xmlStrcat(ret, node->ns->prefix);
1323 ret = xmlStrcat(ret, BAD_CAST ":");
1324 ret = xmlStrcat(ret, node->name);
1327 child = child->next;
1332 * remove superfluous \n
1335 int len = xmlStrlen(ret);
1340 if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1341 while ((c == ' ') || (c == '\n') ||
1342 (c == '\r') || (c == '\t')) {
1354 child = child->next;
1360 * xmlSchematronReportSuccess:
1361 * @ctxt: the validation context
1362 * @test: the compiled test
1363 * @cur: the current node tested
1364 * @success: boolean value for the result
1366 * called from the validation engine when an assert or report test have
1370 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
1371 xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
1372 if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1374 /* if quiet and not SVRL report only failures */
1375 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1376 ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
1377 (test->type == XML_SCHEMATRON_REPORT))
1379 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1385 const xmlChar *report = NULL;
1387 if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
1388 ((test->type == XML_SCHEMATRON_ASSERT) & (success)))
1390 line = xmlGetLineNo(cur);
1391 path = xmlGetNodePath(cur);
1393 path = (xmlChar *) cur->name;
1395 if ((test->report != NULL) && (test->report[0] != 0))
1396 report = test->report;
1398 if (test->node != NULL)
1399 report = xmlSchematronFormatReport(ctxt, test->node, cur);
1400 if (report == NULL) {
1401 if (test->type == XML_SCHEMATRON_ASSERT) {
1402 report = xmlStrdup((const xmlChar *) "node failed assert");
1404 report = xmlStrdup((const xmlChar *) "node failed report");
1407 snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1408 line, (const char *) report);
1410 if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
1411 xmlStructuredErrorFunc schannel = NULL;
1412 xmlGenericErrorFunc channel = NULL;
1416 if (ctxt->serror != NULL)
1417 schannel = ctxt->serror;
1419 channel = ctxt->error;
1420 data = ctxt->userData;
1423 __xmlRaiseError(schannel, channel, data,
1424 NULL, cur, XML_FROM_SCHEMATRONV,
1425 (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,
1426 XML_ERR_ERROR, NULL, line,
1427 (pattern == NULL)?NULL:((const char *) pattern->name),
1428 (const char *) path,
1429 (const char *) report, 0, 0,
1432 xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1435 xmlFree((char *) report);
1437 if ((path != NULL) && (path != (xmlChar *) cur->name))
1443 * xmlSchematronReportPattern:
1444 * @ctxt: the validation context
1445 * @pattern: the current pattern
1447 * called from the validation engine when starting to check a pattern
1450 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1451 xmlSchematronPatternPtr pattern) {
1452 if ((ctxt == NULL) || (pattern == NULL))
1454 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
1456 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1461 if (pattern->name == NULL)
1463 snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1464 xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1469 /************************************************************************
1471 * Validation against a Schematrontron *
1473 ************************************************************************/
1476 * xmlSchematronSetValidStructuredErrors:
1477 * @ctxt: a Schematron validation context
1478 * @serror: the structured error function
1479 * @ctx: the functions context
1481 * Set the structured error callback
1484 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
1485 xmlStructuredErrorFunc serror, void *ctx)
1489 ctxt->serror = serror;
1491 ctxt->warning = NULL;
1492 ctxt->userData = ctx;
1496 * xmlSchematronNewValidCtxt:
1497 * @schema: a precompiled XML Schematrons
1498 * @options: a set of xmlSchematronValidOptions
1500 * Create an XML Schematrons validation context based on the given schema.
1502 * Returns the validation context or NULL in case of error
1504 xmlSchematronValidCtxtPtr
1505 xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1508 xmlSchematronValidCtxtPtr ret;
1510 ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
1512 xmlSchematronVErrMemory(NULL, "allocating validation context",
1516 memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1517 ret->type = XML_STRON_CTXT_VALIDATOR;
1518 ret->schema = schema;
1519 ret->xctxt = xmlXPathNewContext(NULL);
1520 ret->flags = options;
1521 if (ret->xctxt == NULL) {
1522 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
1524 xmlSchematronFreeValidCtxt(ret);
1527 for (i = 0;i < schema->nbNamespaces;i++) {
1528 if ((schema->namespaces[2 * i] == NULL) ||
1529 (schema->namespaces[2 * i + 1] == NULL))
1531 xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1532 schema->namespaces[2 * i]);
1538 * xmlSchematronFreeValidCtxt:
1539 * @ctxt: the schema validation context
1541 * Free the resources associated to the schema validation context
1544 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1548 if (ctxt->xctxt != NULL)
1549 xmlXPathFreeContext(ctxt->xctxt);
1550 if (ctxt->dict != NULL)
1551 xmlDictFree(ctxt->dict);
1556 xmlSchematronNextNode(xmlNodePtr cur) {
1557 if (cur->children != NULL) {
1559 * Do not descend on entities declarations
1561 if (cur->children->type != XML_ENTITY_DECL) {
1562 cur = cur->children;
1566 if (cur->type != XML_DTD_NODE)
1571 while (cur->next != NULL) {
1573 if ((cur->type != XML_ENTITY_DECL) &&
1574 (cur->type != XML_DTD_NODE))
1580 if (cur == NULL) break;
1581 if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1582 if (cur->next != NULL) {
1586 } while (cur != NULL);
1591 * xmlSchematronRunTest:
1592 * @ctxt: the schema validation context
1593 * @test: the current test
1594 * @instance: the document instace tree
1595 * @cur: the current node in the instance
1597 * Validate a rule against a tree instance at a given position
1599 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1602 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1603 xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
1605 xmlXPathObjectPtr ret;
1609 ctxt->xctxt->doc = instance;
1610 ctxt->xctxt->node = cur;
1611 ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1615 switch (ret->type) {
1616 case XPATH_XSLT_TREE:
1618 if ((ret->nodesetval == NULL) ||
1619 (ret->nodesetval->nodeNr == 0))
1623 failed = !ret->boolval;
1626 if ((xmlXPathIsNaN(ret->floatval)) ||
1627 (ret->floatval == 0.0))
1631 if ((ret->stringval == NULL) ||
1632 (ret->stringval[0] == 0))
1635 case XPATH_UNDEFINED:
1638 case XPATH_LOCATIONSET:
1643 xmlXPathFreeObject(ret);
1645 if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
1647 else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1650 xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
1656 * xmlSchematronValidateDoc:
1657 * @ctxt: the schema validation context
1658 * @instance: the document instace tree
1660 * Validate a tree instance against the schematron
1662 * Returns 0 in case of success, -1 in case of internal error
1663 * and an error count otherwise.
1666 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1668 xmlNodePtr cur, root;
1669 xmlSchematronPatternPtr pattern;
1670 xmlSchematronRulePtr rule;
1671 xmlSchematronTestPtr test;
1673 if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1674 (ctxt->schema->rules == NULL) || (instance == NULL))
1677 root = xmlDocGetRootElement(instance);
1683 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1684 (ctxt->flags == 0)) {
1686 * we are just trying to assert the validity of the document,
1687 * speed primes over the output, run in a single pass
1690 while (cur != NULL) {
1691 rule = ctxt->schema->rules;
1692 while (rule != NULL) {
1693 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1695 while (test != NULL) {
1696 xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
1703 cur = xmlSchematronNextNode(cur);
1707 * Process all contexts one at a time
1709 pattern = ctxt->schema->patterns;
1711 while (pattern != NULL) {
1712 xmlSchematronReportPattern(ctxt, pattern);
1715 * TODO convert the pattern rule to a direct XPath and
1716 * compute directly instead of using the pattern matching
1717 * over the full document...
1718 * Check the exact semantic
1721 while (cur != NULL) {
1722 rule = pattern->rules;
1723 while (rule != NULL) {
1724 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1726 while (test != NULL) {
1727 xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
1731 rule = rule->patnext;
1734 cur = xmlSchematronNextNode(cur);
1736 pattern = pattern->next;
1739 return(ctxt->nberrors);
1748 xmlSchematronParserCtxtPtr pctxt;
1749 xmlSchematronValidCtxtPtr vctxt;
1750 xmlSchematronPtr schema = NULL;
1752 pctxt = xmlSchematronNewParserCtxt("tst.sct");
1753 if (pctxt == NULL) {
1754 fprintf(stderr, "failed to build schematron parser\n");
1756 schema = xmlSchematronParse(pctxt);
1757 if (schema == NULL) {
1758 fprintf(stderr, "failed to compile schematron\n");
1760 xmlSchematronFreeParserCtxt(pctxt);
1762 instance = xmlReadFile("tst.sct", NULL,
1763 XML_PARSE_NOENT | XML_PARSE_NOCDATA);
1764 if (instance == NULL) {
1765 fprintf(stderr, "failed to parse instance\n");
1767 if ((schema != NULL) && (instance != NULL)) {
1768 vctxt = xmlSchematronNewValidCtxt(schema);
1769 if (vctxt == NULL) {
1770 fprintf(stderr, "failed to build schematron validator\n");
1772 ret = xmlSchematronValidateDoc(vctxt, instance);
1773 xmlSchematronFreeValidCtxt(vctxt);
1776 xmlSchematronFree(schema);
1777 xmlFreeDoc(instance);
1785 #define bottom_schematron
1786 #include "elfgcchack.h"
1787 #endif /* LIBXML_SCHEMATRON_ENABLED */