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;
172 /* error reporting data */
173 void *userData; /* user specific data block */
174 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
175 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
176 xmlStructuredErrorFunc serror; /* the structured function */
179 struct _xmlSchematronParserCtxt {
183 int preserve; /* Whether the doc should be freed */
187 xmlDictPtr dict; /* dictionnary for interned string names */
191 xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
192 xmlSchematronPtr schema;
194 int nbNamespaces; /* number of namespaces in the array */
195 int maxNamespaces; /* size of the array */
196 const xmlChar **namespaces; /* the array of namespaces */
198 int nbIncludes; /* number of includes in the array */
199 int maxIncludes; /* size of the array */
200 xmlNodePtr *includes; /* the array of includes */
202 /* error reporting data */
203 void *userData; /* user specific data block */
204 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
205 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
206 xmlStructuredErrorFunc serror; /* the structured function */
209 #define XML_STRON_CTXT_PARSER 1
210 #define XML_STRON_CTXT_VALIDATOR 2
212 /************************************************************************
216 ************************************************************************/
219 * xmlSchematronPErrMemory:
220 * @node: a context node
221 * @extra: extra informations
223 * Handle an out of memory condition
226 xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
227 const char *extra, xmlNodePtr node)
231 __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
237 * @ctxt: the parsing context
238 * @node: the context node
239 * @error: the error code
240 * @msg: the error message
244 * Handle a parser error
247 xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
248 const char *msg, const xmlChar * str1, const xmlChar * str2)
250 xmlGenericErrorFunc channel = NULL;
251 xmlStructuredErrorFunc schannel = NULL;
256 channel = ctxt->error;
257 data = ctxt->userData;
258 schannel = ctxt->serror;
260 __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
261 error, XML_ERR_ERROR, NULL, 0,
262 (const char *) str1, (const char *) str2, NULL, 0, 0,
267 * xmlSchematronVTypeErrMemory:
268 * @node: a context node
269 * @extra: extra informations
271 * Handle an out of memory condition
274 xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
275 const char *extra, xmlNodePtr node)
279 ctxt->err = XML_SCHEMAV_INTERNAL;
281 __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
285 /************************************************************************
287 * Parsing and compilation of the Schematrontrons *
289 ************************************************************************/
292 * xmlSchematronAddTest:
293 * @ctxt: the schema parsing context
294 * @type: the type of test
295 * @rule: the parent rule
296 * @node: the node hosting the test
297 * @test: the associated test
298 * @report: the associated report string
300 * Add a test to a schematron
302 * Returns the new pointer or NULL in case of error
304 static xmlSchematronTestPtr
305 xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
306 xmlSchematronTestType type,
307 xmlSchematronRulePtr rule,
308 xmlNodePtr node, xmlChar *test, xmlChar *report)
310 xmlSchematronTestPtr ret;
311 xmlXPathCompExprPtr comp;
313 if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
318 * try first to compile the test expression
320 comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
322 xmlSchematronPErr(ctxt, node,
324 "Failed to compile test expression %s",
329 ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
331 xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
334 memset(ret, 0, sizeof(xmlSchematronTest));
339 ret->report = report;
341 if (rule->tests == NULL) {
344 xmlSchematronTestPtr prev = rule->tests;
346 while (prev->next != NULL)
354 * xmlSchematronFreeTests:
355 * @tests: a list of tests
357 * Free a list of tests.
360 xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
361 xmlSchematronTestPtr next;
363 while (tests != NULL) {
365 if (tests->test != NULL)
366 xmlFree(tests->test);
367 if (tests->comp != NULL)
368 xmlXPathFreeCompExpr(tests->comp);
369 if (tests->report != NULL)
370 xmlFree(tests->report);
377 * xmlSchematronAddRule:
378 * @ctxt: the schema parsing context
379 * @schema: a schema structure
380 * @node: the node hosting the rule
381 * @context: the associated context string
382 * @report: the associated report string
384 * Add a rule to a schematron
386 * Returns the new pointer or NULL in case of error
388 static xmlSchematronRulePtr
389 xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
390 xmlSchematronPatternPtr pat, xmlNodePtr node,
391 xmlChar *context, xmlChar *report)
393 xmlSchematronRulePtr ret;
394 xmlPatternPtr pattern;
396 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
401 * Try first to compile the pattern
403 pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
405 if (pattern == NULL) {
406 xmlSchematronPErr(ctxt, node,
408 "Failed to compile context expression %s",
412 ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
414 xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
417 memset(ret, 0, sizeof(xmlSchematronRule));
419 ret->context = context;
420 ret->pattern = pattern;
421 ret->report = report;
423 if (schema->rules == NULL) {
426 xmlSchematronRulePtr prev = schema->rules;
428 while (prev->next != NULL)
433 if (pat->rules == NULL) {
436 xmlSchematronRulePtr prev = pat->rules;
438 while (prev->patnext != NULL)
439 prev = prev->patnext;
446 * xmlSchematronFreeRules:
447 * @rules: a list of rules
449 * Free a list of rules.
452 xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
453 xmlSchematronRulePtr next;
455 while (rules != NULL) {
458 xmlSchematronFreeTests(rules->tests);
459 if (rules->context != NULL)
460 xmlFree(rules->context);
462 xmlFreePattern(rules->pattern);
463 if (rules->report != NULL)
464 xmlFree(rules->report);
471 * xmlSchematronAddPattern:
472 * @ctxt: the schema parsing context
473 * @schema: a schema structure
474 * @node: the node hosting the pattern
475 * @id: the id or name of the pattern
477 * Add a pattern to a schematron
479 * Returns the new pointer or NULL in case of error
481 static xmlSchematronPatternPtr
482 xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
483 xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
485 xmlSchematronPatternPtr ret;
487 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
490 ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
492 xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
495 memset(ret, 0, sizeof(xmlSchematronPattern));
498 if (schema->patterns == NULL) {
499 schema->patterns = ret;
501 xmlSchematronPatternPtr prev = schema->patterns;
503 while (prev->next != NULL)
511 * xmlSchematronFreePatterns:
512 * @patterns: a list of patterns
514 * Free a list of patterns.
517 xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
518 xmlSchematronPatternPtr next;
520 while (patterns != NULL) {
521 next = patterns->next;
522 if (patterns->name != NULL)
523 xmlFree(patterns->name);
530 * xmlSchematronNewSchematron:
531 * @ctxt: a schema validation context
533 * Allocate a new Schematron structure.
535 * Returns the newly allocated structure or NULL in case or error
537 static xmlSchematronPtr
538 xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
540 xmlSchematronPtr ret;
542 ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
544 xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
547 memset(ret, 0, sizeof(xmlSchematron));
548 ret->dict = ctxt->dict;
549 xmlDictReference(ret->dict);
556 * @schema: a schema structure
558 * Deallocate a Schematron structure.
561 xmlSchematronFree(xmlSchematronPtr schema)
566 if ((schema->doc != NULL) && (!(schema->preserve)))
567 xmlFreeDoc(schema->doc);
569 if (schema->namespaces != NULL)
570 xmlFree((char **) schema->namespaces);
572 xmlSchematronFreeRules(schema->rules);
573 xmlSchematronFreePatterns(schema->patterns);
574 xmlDictFree(schema->dict);
579 * xmlSchematronNewParserCtxt:
580 * @URL: the location of the schema
582 * Create an XML Schematrons parse context for that file/resource expected
583 * to contain an XML Schematrons file.
585 * Returns the parser context or NULL in case of error
587 xmlSchematronParserCtxtPtr
588 xmlSchematronNewParserCtxt(const char *URL)
590 xmlSchematronParserCtxtPtr ret;
596 (xmlSchematronParserCtxtPtr)
597 xmlMalloc(sizeof(xmlSchematronParserCtxt));
599 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
603 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
604 ret->type = XML_STRON_CTXT_PARSER;
605 ret->dict = xmlDictCreate();
606 ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
607 ret->includes = NULL;
608 ret->xctxt = xmlXPathNewContext(NULL);
609 if (ret->xctxt == NULL) {
610 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
612 xmlSchematronFreeParserCtxt(ret);
615 ret->xctxt->flags = XML_XPATH_CHECKNS;
620 * xmlSchematronNewMemParserCtxt:
621 * @buffer: a pointer to a char array containing the schemas
622 * @size: the size of the array
624 * Create an XML Schematrons parse context for that memory buffer expected
625 * to contain an XML Schematrons file.
627 * Returns the parser context or NULL in case of error
629 xmlSchematronParserCtxtPtr
630 xmlSchematronNewMemParserCtxt(const char *buffer, int size)
632 xmlSchematronParserCtxtPtr ret;
634 if ((buffer == NULL) || (size <= 0))
638 (xmlSchematronParserCtxtPtr)
639 xmlMalloc(sizeof(xmlSchematronParserCtxt));
641 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
645 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
646 ret->buffer = buffer;
648 ret->dict = xmlDictCreate();
649 ret->xctxt = xmlXPathNewContext(NULL);
650 if (ret->xctxt == NULL) {
651 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
653 xmlSchematronFreeParserCtxt(ret);
660 * xmlSchematronNewDocParserCtxt:
661 * @doc: a preparsed document tree
663 * Create an XML Schematrons parse context for that document.
664 * NB. The document may be modified during the parsing process.
666 * Returns the parser context or NULL in case of error
668 xmlSchematronParserCtxtPtr
669 xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
671 xmlSchematronParserCtxtPtr ret;
677 (xmlSchematronParserCtxtPtr)
678 xmlMalloc(sizeof(xmlSchematronParserCtxt));
680 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
684 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
686 ret->dict = xmlDictCreate();
687 /* The application has responsibility for the document */
689 ret->xctxt = xmlXPathNewContext(doc);
690 if (ret->xctxt == NULL) {
691 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
693 xmlSchematronFreeParserCtxt(ret);
701 * xmlSchematronFreeParserCtxt:
702 * @ctxt: the schema parser context
704 * Free the resources associated to the schema parser context
707 xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
711 if (ctxt->doc != NULL && !ctxt->preserve)
712 xmlFreeDoc(ctxt->doc);
713 if (ctxt->xctxt != NULL) {
714 xmlXPathFreeContext(ctxt->xctxt);
716 if (ctxt->namespaces != NULL)
717 xmlFree((char **) ctxt->namespaces);
718 xmlDictFree(ctxt->dict);
724 * xmlSchematronPushInclude:
725 * @ctxt: the schema parser context
726 * @doc: the included document
727 * @cur: the current include node
729 * Add an included document
732 xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
733 xmlDocPtr doc, xmlNodePtr cur)
735 if (ctxt->includes == NULL) {
736 ctxt->maxIncludes = 10;
737 ctxt->includes = (xmlNodePtr *)
738 xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
739 if (ctxt->includes == NULL) {
740 xmlSchematronPErrMemory(NULL, "allocating parser includes",
744 ctxt->nbIncludes = 0;
745 } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
749 xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
752 xmlSchematronPErrMemory(NULL, "allocating parser includes",
756 ctxt->includes = tmp;
757 ctxt->maxIncludes *= 2;
759 ctxt->includes[2 * ctxt->nbIncludes] = cur;
760 ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
765 * xmlSchematronPopInclude:
766 * @ctxt: the schema parser context
768 * Pop an include level. The included document is being freed
770 * Returns the node immediately following the include or NULL if the
771 * include list was empty.
774 xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
779 if (ctxt->nbIncludes <= 0)
782 doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
783 ret = ctxt->includes[2 * ctxt->nbIncludes];
788 return(xmlSchematronPopInclude(ctxt));
794 * xmlSchematronAddNamespace:
795 * @ctxt: the schema parser context
796 * @prefix: the namespace prefix
797 * @ns: the namespace name
799 * Add a namespace definition in the context
802 xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
803 const xmlChar *prefix, const xmlChar *ns)
805 if (ctxt->namespaces == NULL) {
806 ctxt->maxNamespaces = 10;
807 ctxt->namespaces = (const xmlChar **)
808 xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
809 if (ctxt->namespaces == NULL) {
810 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
814 ctxt->nbNamespaces = 0;
815 } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
818 tmp = (const xmlChar **)
819 xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
820 sizeof(const xmlChar *));
822 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
826 ctxt->namespaces = tmp;
827 ctxt->maxNamespaces *= 2;
829 ctxt->namespaces[2 * ctxt->nbNamespaces] =
830 xmlDictLookup(ctxt->dict, ns, -1);
831 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
832 xmlDictLookup(ctxt->dict, prefix, -1);
833 ctxt->nbNamespaces++;
834 ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
835 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
840 * xmlSchematronParseRule:
841 * @ctxt: a schema validation context
842 * @rule: the rule node
844 * parse a rule element
847 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
848 xmlSchematronPatternPtr pattern,
856 xmlSchematronRulePtr ruleptr;
857 xmlSchematronTestPtr testptr;
859 if ((ctxt == NULL) || (rule == NULL)) return;
861 context = xmlGetNoNsProp(rule, BAD_CAST "context");
862 if (context == NULL) {
863 xmlSchematronPErr(ctxt, rule,
865 "rule has no context attribute",
868 } else if (context[0] == 0) {
869 xmlSchematronPErr(ctxt, rule,
871 "rule has an empty context attribute",
876 ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
877 rule, context, NULL);
878 if (ruleptr == NULL) {
884 cur = rule->children;
885 NEXT_SCHEMATRON(cur);
886 while (cur != NULL) {
887 if (IS_SCHEMATRON(cur, "assert")) {
889 test = xmlGetNoNsProp(cur, BAD_CAST "test");
891 xmlSchematronPErr(ctxt, cur,
893 "assert has no test attribute",
895 } else if (test[0] == 0) {
896 xmlSchematronPErr(ctxt, cur,
898 "assert has an empty test attribute",
902 /* TODO will need dynamic processing instead */
903 report = xmlNodeGetContent(cur);
905 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
906 ruleptr, cur, test, report);
910 } else if (IS_SCHEMATRON(cur, "report")) {
912 test = xmlGetNoNsProp(cur, BAD_CAST "test");
914 xmlSchematronPErr(ctxt, cur,
916 "assert has no test attribute",
918 } else if (test[0] == 0) {
919 xmlSchematronPErr(ctxt, cur,
921 "assert has an empty test attribute",
925 /* TODO will need dynamic processing instead */
926 report = xmlNodeGetContent(cur);
928 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
929 ruleptr, cur, test, report);
934 xmlSchematronPErr(ctxt, cur,
936 "Expecting an assert or a report element instead of %s",
940 NEXT_SCHEMATRON(cur);
943 xmlSchematronPErr(ctxt, rule,
945 "rule has no assert nor report element", NULL, NULL);
950 * xmlSchematronParsePattern:
951 * @ctxt: a schema validation context
952 * @pat: the pattern node
954 * parse a pattern element
957 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
960 xmlSchematronPatternPtr pattern;
964 if ((ctxt == NULL) || (pat == NULL)) return;
966 id = xmlGetNoNsProp(pat, BAD_CAST "id");
968 id = xmlGetNoNsProp(pat, BAD_CAST "name");
970 pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
971 if (pattern == NULL) {
977 NEXT_SCHEMATRON(cur);
978 while (cur != NULL) {
979 if (IS_SCHEMATRON(cur, "rule")) {
980 xmlSchematronParseRule(ctxt, pattern, cur);
983 xmlSchematronPErr(ctxt, cur,
985 "Expecting a rule element instead of %s", cur->name, NULL);
988 NEXT_SCHEMATRON(cur);
991 xmlSchematronPErr(ctxt, pat,
993 "Pattern has no rule element", NULL, NULL);
999 * xmlSchematronLoadInclude:
1000 * @ctxt: a schema validation context
1001 * @cur: the include element
1003 * Load the include document, Push the current pointer
1005 * Returns the updated node pointer
1008 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1010 xmlNodePtr ret = NULL;
1011 xmlDocPtr doc = NULL;
1012 xmlChar *href = NULL;
1013 xmlChar *base = NULL;
1014 xmlChar *URI = NULL;
1016 if ((ctxt == NULL) || (cur == NULL))
1019 href = xmlGetNoNsProp(cur, BAD_CAST "href");
1021 xmlSchematronPErr(ctxt, cur,
1023 "Include has no href attribute", NULL, NULL);
1027 /* do the URI base composition, load and find the root */
1028 base = xmlNodeGetBase(cur->doc, cur);
1029 URI = xmlBuildURI(href, base);
1030 doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
1032 xmlSchematronPErr(ctxt, cur,
1033 XML_SCHEMAP_FAILED_LOAD,
1034 "could not load include '%s'.\n",
1038 ret = xmlDocGetRootElement(doc);
1040 xmlSchematronPErr(ctxt, cur,
1041 XML_SCHEMAP_FAILED_LOAD,
1042 "could not find root from include '%s'.\n",
1047 /* Success, push the include for rollback on exit */
1048 xmlSchematronPushInclude(ctxt, doc, cur);
1065 * xmlSchematronParse:
1066 * @ctxt: a schema validation context
1068 * parse a schema definition resource and build an internal
1069 * XML Shema struture which can be used to validate instances.
1071 * Returns the internal XML Schematron structure built from the resource or
1072 * NULL in case of error
1075 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1077 xmlSchematronPtr ret = NULL;
1079 xmlNodePtr root, cur;
1088 * First step is to parse the input document into an DOM/Infoset
1090 if (ctxt->URL != NULL) {
1091 doc = xmlReadFile((const char *) ctxt->URL, NULL,
1092 SCHEMATRON_PARSE_OPTIONS);
1094 xmlSchematronPErr(ctxt, NULL,
1095 XML_SCHEMAP_FAILED_LOAD,
1096 "xmlSchematronParse: could not load '%s'.\n",
1101 } else if (ctxt->buffer != NULL) {
1102 doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1103 SCHEMATRON_PARSE_OPTIONS);
1105 xmlSchematronPErr(ctxt, NULL,
1106 XML_SCHEMAP_FAILED_PARSE,
1107 "xmlSchematronParse: could not parse.\n",
1111 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1112 ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
1114 } else if (ctxt->doc != NULL) {
1119 xmlSchematronPErr(ctxt, NULL,
1120 XML_SCHEMAP_NOTHING_TO_PARSE,
1121 "xmlSchematronParse: could not parse.\n",
1127 * Then extract the root and Schematron parse it
1129 root = xmlDocGetRootElement(doc);
1131 xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1133 "The schema has no document element.\n", NULL, NULL);
1140 if (!IS_SCHEMATRON(root, "schema")) {
1141 xmlSchematronPErr(ctxt, root,
1143 "The XML document '%s' is not a XML schematron document",
1147 ret = xmlSchematronNewSchematron(ctxt);
1153 * scan the schema elements
1155 cur = root->children;
1156 NEXT_SCHEMATRON(cur);
1157 if (IS_SCHEMATRON(cur, "title")) {
1158 xmlChar *title = xmlNodeGetContent(cur);
1159 if (title != NULL) {
1160 ret->title = xmlDictLookup(ret->dict, title, -1);
1164 NEXT_SCHEMATRON(cur);
1166 while (IS_SCHEMATRON(cur, "ns")) {
1167 xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1168 xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1169 if ((uri == NULL) || (uri[0] == 0)) {
1170 xmlSchematronPErr(ctxt, cur,
1172 "ns element has no uri", NULL, NULL);
1174 if ((prefix == NULL) || (prefix[0] == 0)) {
1175 xmlSchematronPErr(ctxt, cur,
1177 "ns element has no prefix", NULL, NULL);
1179 if ((prefix) && (uri)) {
1180 xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1181 xmlSchematronAddNamespace(ctxt, prefix, uri);
1189 NEXT_SCHEMATRON(cur);
1191 while (cur != NULL) {
1192 if (IS_SCHEMATRON(cur, "pattern")) {
1193 xmlSchematronParsePattern(ctxt, cur);
1196 xmlSchematronPErr(ctxt, cur,
1198 "Expecting a pattern element instead of %s", cur->name, NULL);
1201 NEXT_SCHEMATRON(cur);
1203 if (ret->nbPattern == 0) {
1204 xmlSchematronPErr(ctxt, root,
1206 "The schematron document '%s' has no pattern",
1210 /* the original document must be kept for reporting */
1222 if (ctxt->nberrors != 0) {
1223 xmlSchematronFree(ret);
1226 ret->namespaces = ctxt->namespaces;
1227 ret->nbNamespaces = ctxt->nbNamespaces;
1228 ctxt->namespaces = NULL;
1234 /************************************************************************
1236 * Schematrontron Reports handler *
1238 ************************************************************************/
1241 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1242 xmlNodePtr cur, const xmlChar *xpath) {
1243 xmlNodePtr node = NULL;
1244 xmlXPathObjectPtr ret;
1246 if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1249 ctxt->xctxt->doc = cur->doc;
1250 ctxt->xctxt->node = cur;
1251 ret = xmlXPathEval(xpath, ctxt->xctxt);
1255 if ((ret->type == XPATH_NODESET) &&
1256 (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1257 node = ret->nodesetval->nodeTab[0];
1259 xmlXPathFreeObject(ret);
1264 * xmlSchematronReportOutput:
1265 * @ctxt: the validation context
1266 * @cur: the current node tested
1267 * @msg: the message output
1269 * Output part of the report to whatever channel the user selected
1272 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1273 xmlNodePtr cur ATTRIBUTE_UNUSED,
1276 fprintf(stderr, "%s", msg);
1280 * xmlSchematronFormatReport:
1281 * @ctxt: the validation context
1282 * @test: the test node
1283 * @cur: the current node tested
1285 * Build the string being reported to the user.
1287 * Returns a report string or NULL in case of error. The string needs
1288 * to be deallocated by teh caller
1291 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1292 xmlNodePtr test, xmlNodePtr cur) {
1293 xmlChar *ret = NULL;
1294 xmlNodePtr child, node;
1296 if ((test == NULL) || (cur == NULL))
1299 child = test->children;
1300 while (child != NULL) {
1301 if ((child->type == XML_TEXT_NODE) ||
1302 (child->type == XML_CDATA_SECTION_NODE))
1303 ret = xmlStrcat(ret, child->content);
1304 else if (IS_SCHEMATRON(child, "name")) {
1307 path = xmlGetNoNsProp(child, BAD_CAST "path");
1311 node = xmlSchematronGetNode(ctxt, cur, path);
1317 if ((node->ns == NULL) || (node->ns->prefix == NULL))
1318 ret = xmlStrcat(ret, node->name);
1320 ret = xmlStrcat(ret, node->ns->prefix);
1321 ret = xmlStrcat(ret, BAD_CAST ":");
1322 ret = xmlStrcat(ret, node->name);
1325 child = child->next;
1330 * remove superfluous \n
1333 int len = xmlStrlen(ret);
1338 if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1339 while ((c == ' ') || (c == '\n') ||
1340 (c == '\r') || (c == '\t')) {
1352 child = child->next;
1358 * xmlSchematronReportSuccess:
1359 * @ctxt: the validation context
1360 * @test: the compiled test
1361 * @cur: the current node tested
1362 * @success: boolean value for the result
1364 * called from the validation engine when an assert or report test have
1368 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
1369 xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
1370 if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1372 /* if quiet and not SVRL report only failures */
1373 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1374 ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
1375 (test->type == XML_SCHEMATRON_REPORT))
1377 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1383 const xmlChar *report = NULL;
1385 if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
1386 ((test->type == XML_SCHEMATRON_ASSERT) & (success)))
1388 line = xmlGetLineNo(cur);
1389 path = xmlGetNodePath(cur);
1391 path = (xmlChar *) cur->name;
1393 if ((test->report != NULL) && (test->report[0] != 0))
1394 report = test->report;
1396 if (test->node != NULL)
1397 report = xmlSchematronFormatReport(ctxt, test->node, cur);
1398 if (report == NULL) {
1399 if (test->type == XML_SCHEMATRON_ASSERT) {
1400 report = xmlStrdup((const xmlChar *) "node failed assert");
1402 report = xmlStrdup((const xmlChar *) "node failed report");
1405 snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1406 line, (const char *) report);
1408 if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
1409 xmlStructuredErrorFunc schannel = NULL;
1410 xmlGenericErrorFunc channel = NULL;
1414 if (ctxt->serror != NULL)
1415 schannel = ctxt->serror;
1417 channel = ctxt->error;
1418 data = ctxt->userData;
1421 __xmlRaiseError(schannel, channel, data,
1422 NULL, cur, XML_FROM_SCHEMATRONV,
1423 (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,
1424 XML_ERR_ERROR, NULL, line,
1425 (pattern == NULL)?NULL:((const char *) pattern->name),
1426 (const char *) path,
1427 (const char *) report, 0, 0,
1430 xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1433 xmlFree((char *) report);
1435 if ((path != NULL) && (path != (xmlChar *) cur->name))
1441 * xmlSchematronReportPattern:
1442 * @ctxt: the validation context
1443 * @pattern: the current pattern
1445 * called from the validation engine when starting to check a pattern
1448 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1449 xmlSchematronPatternPtr pattern) {
1450 if ((ctxt == NULL) || (pattern == NULL))
1452 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
1454 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1459 if (pattern->name == NULL)
1461 snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1462 xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1467 /************************************************************************
1469 * Validation against a Schematrontron *
1471 ************************************************************************/
1474 * xmlSchematronSetValidStructuredErrors:
1475 * @ctxt: a Schematron validation context
1476 * @serror: the structured error function
1477 * @ctx: the functions context
1479 * Set the structured error callback
1482 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
1483 xmlStructuredErrorFunc serror, void *ctx)
1487 ctxt->serror = serror;
1489 ctxt->warning = NULL;
1490 ctxt->userData = ctx;
1494 * xmlSchematronNewValidCtxt:
1495 * @schema: a precompiled XML Schematrons
1496 * @options: a set of xmlSchematronValidOptions
1498 * Create an XML Schematrons validation context based on the given schema.
1500 * Returns the validation context or NULL in case of error
1502 xmlSchematronValidCtxtPtr
1503 xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1506 xmlSchematronValidCtxtPtr ret;
1508 ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
1510 xmlSchematronVErrMemory(NULL, "allocating validation context",
1514 memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1515 ret->type = XML_STRON_CTXT_VALIDATOR;
1516 ret->schema = schema;
1517 ret->xctxt = xmlXPathNewContext(NULL);
1518 ret->flags = options;
1519 if (ret->xctxt == NULL) {
1520 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
1522 xmlSchematronFreeValidCtxt(ret);
1525 for (i = 0;i < schema->nbNamespaces;i++) {
1526 if ((schema->namespaces[2 * i] == NULL) ||
1527 (schema->namespaces[2 * i + 1] == NULL))
1529 xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1530 schema->namespaces[2 * i]);
1536 * xmlSchematronFreeValidCtxt:
1537 * @ctxt: the schema validation context
1539 * Free the resources associated to the schema validation context
1542 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1546 if (ctxt->xctxt != NULL)
1547 xmlXPathFreeContext(ctxt->xctxt);
1548 if (ctxt->dict != NULL)
1549 xmlDictFree(ctxt->dict);
1554 xmlSchematronNextNode(xmlNodePtr cur) {
1555 if (cur->children != NULL) {
1557 * Do not descend on entities declarations
1559 if (cur->children->type != XML_ENTITY_DECL) {
1560 cur = cur->children;
1564 if (cur->type != XML_DTD_NODE)
1569 while (cur->next != NULL) {
1571 if ((cur->type != XML_ENTITY_DECL) &&
1572 (cur->type != XML_DTD_NODE))
1578 if (cur == NULL) break;
1579 if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1580 if (cur->next != NULL) {
1584 } while (cur != NULL);
1589 * xmlSchematronRunTest:
1590 * @ctxt: the schema validation context
1591 * @test: the current test
1592 * @instance: the document instace tree
1593 * @cur: the current node in the instance
1595 * Validate a rule against a tree instance at a given position
1597 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1600 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1601 xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
1603 xmlXPathObjectPtr ret;
1607 ctxt->xctxt->doc = instance;
1608 ctxt->xctxt->node = cur;
1609 ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1613 switch (ret->type) {
1614 case XPATH_XSLT_TREE:
1616 if ((ret->nodesetval == NULL) ||
1617 (ret->nodesetval->nodeNr == 0))
1621 failed = !ret->boolval;
1624 if ((xmlXPathIsNaN(ret->floatval)) ||
1625 (ret->floatval == 0.0))
1629 if ((ret->stringval == NULL) ||
1630 (ret->stringval[0] == 0))
1633 case XPATH_UNDEFINED:
1636 case XPATH_LOCATIONSET:
1641 xmlXPathFreeObject(ret);
1643 if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
1645 else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1648 xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
1654 * xmlSchematronValidateDoc:
1655 * @ctxt: the schema validation context
1656 * @instance: the document instace tree
1658 * Validate a tree instance against the schematron
1660 * Returns 0 in case of success, -1 in case of internal error
1661 * and an error count otherwise.
1664 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1666 xmlNodePtr cur, root;
1667 xmlSchematronPatternPtr pattern;
1668 xmlSchematronRulePtr rule;
1669 xmlSchematronTestPtr test;
1671 if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1672 (ctxt->schema->rules == NULL) || (instance == NULL))
1675 root = xmlDocGetRootElement(instance);
1681 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1682 (ctxt->flags == 0)) {
1684 * we are just trying to assert the validity of the document,
1685 * speed primes over the output, run in a single pass
1688 while (cur != NULL) {
1689 rule = ctxt->schema->rules;
1690 while (rule != NULL) {
1691 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1693 while (test != NULL) {
1694 xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
1701 cur = xmlSchematronNextNode(cur);
1705 * Process all contexts one at a time
1707 pattern = ctxt->schema->patterns;
1709 while (pattern != NULL) {
1710 xmlSchematronReportPattern(ctxt, pattern);
1713 * TODO convert the pattern rule to a direct XPath and
1714 * compute directly instead of using the pattern matching
1715 * over the full document...
1716 * Check the exact semantic
1719 while (cur != NULL) {
1720 rule = pattern->rules;
1721 while (rule != NULL) {
1722 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1724 while (test != NULL) {
1725 xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
1729 rule = rule->patnext;
1732 cur = xmlSchematronNextNode(cur);
1734 pattern = pattern->next;
1737 return(ctxt->nberrors);
1746 xmlSchematronParserCtxtPtr pctxt;
1747 xmlSchematronValidCtxtPtr vctxt;
1748 xmlSchematronPtr schema = NULL;
1750 pctxt = xmlSchematronNewParserCtxt("tst.sct");
1751 if (pctxt == NULL) {
1752 fprintf(stderr, "failed to build schematron parser\n");
1754 schema = xmlSchematronParse(pctxt);
1755 if (schema == NULL) {
1756 fprintf(stderr, "failed to compile schematron\n");
1758 xmlSchematronFreeParserCtxt(pctxt);
1760 instance = xmlReadFile("tst.sct", NULL,
1761 XML_PARSE_NOENT | XML_PARSE_NOCDATA);
1762 if (instance == NULL) {
1763 fprintf(stderr, "failed to parse instance\n");
1765 if ((schema != NULL) && (instance != NULL)) {
1766 vctxt = xmlSchematronNewValidCtxt(schema);
1767 if (vctxt == NULL) {
1768 fprintf(stderr, "failed to build schematron validator\n");
1770 ret = xmlSchematronValidateDoc(vctxt, instance);
1771 xmlSchematronFreeValidCtxt(vctxt);
1774 xmlSchematronFree(schema);
1775 xmlFreeDoc(instance);
1783 #define bottom_schematron
1784 #include "elfgcchack.h"
1785 #endif /* LIBXML_SCHEMATRON_ENABLED */