blob: 0a90ff8019879659b2fd6f77b24f57b03d48341c [file] [log] [blame]
Daniel Veillarded6c5492005-07-23 15:00:22 +00001/*
Daniel Veillarde70375c2005-07-30 21:09:12 +00002 * schematron.c : implementation of the Schematron schema validity checking
Daniel Veillarded6c5492005-07-23 15:00:22 +00003 *
4 * See Copyright for the status of this software.
5 *
Daniel Veillarde70375c2005-07-30 21:09:12 +00006 * Daniel Veillard <daniel@veillard.com>
7 */
8
9/*
10 * TODO:
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
14 * by the spec.
15 * + export of results in SVRL
16 * + full parsing and coverage of the spec, conformance of the input to the
17 * spec
18 * + divergences between the draft and the ISO proposed standard :-(
19 * + hook and test include
20 * + try and compare with the XSLT version
Daniel Veillarded6c5492005-07-23 15:00:22 +000021 */
22
23#define IN_LIBXML
24#include "libxml.h"
25
26#ifdef LIBXML_SCHEMATRON_ENABLED
27
28#include <string.h>
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>
36
37#define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
38
Daniel Veillarde70375c2005-07-30 21:09:12 +000039#define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
40
41#define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
42
43
Daniel Veillarded6c5492005-07-23 15:00:22 +000044static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
Daniel Veillarde70375c2005-07-30 21:09:12 +000045static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
Daniel Veillarded6c5492005-07-23 15:00:22 +000046
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)) && \
Daniel Veillarde70375c2005-07-30 21:09:12 +000051 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
52 (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
Daniel Veillarded6c5492005-07-23 15:00:22 +000053
54#define NEXT_SCHEMATRON(node) \
55 while (node != NULL) { \
56 if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \
Daniel Veillarde70375c2005-07-30 21:09:12 +000057 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
58 (xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) \
Daniel Veillarded6c5492005-07-23 15:00:22 +000059 break; \
60 node = node->next; \
61 }
62
63/**
64 * TODO:
65 *
66 * macro to flag unimplemented blocks
67 */
68#define TODO \
69 xmlGenericError(xmlGenericErrorContext, \
70 "Unimplemented block at %s:%d\n", \
71 __FILE__, __LINE__);
72
Daniel Veillarde70375c2005-07-30 21:09:12 +000073typedef enum {
74 XML_SCHEMATRON_ASSERT=1,
75 XML_SCHEMATRON_REPORT=2
76} xmlSchematronTestType;
77
Daniel Veillarded6c5492005-07-23 15:00:22 +000078/**
79 * _xmlSchematronTest:
80 *
81 * A Schematrons test, either an assert or a report
82 */
83typedef struct _xmlSchematronTest xmlSchematronTest;
84typedef xmlSchematronTest *xmlSchematronTestPtr;
85struct _xmlSchematronTest {
86 xmlSchematronTestPtr next; /* the next test in the list */
Daniel Veillarde70375c2005-07-30 21:09:12 +000087 xmlSchematronTestType type; /* the test type */
Daniel Veillarded6c5492005-07-23 15:00:22 +000088 xmlNodePtr node; /* the node in the tree */
89 xmlChar *test; /* the expression to test */
90 xmlXPathCompExprPtr comp; /* the compiled expression */
Daniel Veillardd4501d72005-07-24 14:27:16 +000091 xmlChar *report; /* the message to report */
Daniel Veillarded6c5492005-07-23 15:00:22 +000092};
93
94/**
95 * _xmlSchematronRule:
96 *
97 * A Schematrons rule
98 */
99typedef struct _xmlSchematronRule xmlSchematronRule;
100typedef xmlSchematronRule *xmlSchematronRulePtr;
101struct _xmlSchematronRule {
102 xmlSchematronRulePtr next; /* the next rule in the list */
Daniel Veillarde70375c2005-07-30 21:09:12 +0000103 xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
Daniel Veillarded6c5492005-07-23 15:00:22 +0000104 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 */
Daniel Veillardd4501d72005-07-24 14:27:16 +0000108 xmlChar *report; /* the message to report */
Daniel Veillarded6c5492005-07-23 15:00:22 +0000109};
110
111/**
Daniel Veillarde70375c2005-07-30 21:09:12 +0000112 * _xmlSchematronPattern:
113 *
114 * A Schematrons pattern
115 */
116typedef struct _xmlSchematronPattern xmlSchematronPattern;
117typedef xmlSchematronPattern *xmlSchematronPatternPtr;
118struct _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 */
122};
123
124/**
Daniel Veillarded6c5492005-07-23 15:00:22 +0000125 * _xmlSchematron:
126 *
127 * A Schematrons definition
128 */
129struct _xmlSchematron {
130 const xmlChar *name; /* schema name */
Daniel Veillardeaecb3e2005-07-31 13:43:14 +0000131 int preserve; /* was the document passed by the user */
Daniel Veillarded6c5492005-07-23 15:00:22 +0000132 xmlDocPtr doc; /* pointer to the parsed document */
133 int flags; /* specific to this schematron */
134
135 void *_private; /* unused by the library */
136 xmlDictPtr dict; /* the dictionnary used internally */
137
138 const xmlChar *title; /* the title if any */
139
140 int nbNs; /* the number of namespaces */
141
142 int nbPattern; /* the number of patterns */
Daniel Veillarde70375c2005-07-30 21:09:12 +0000143 xmlSchematronPatternPtr patterns;/* the patterns found */
Daniel Veillarded6c5492005-07-23 15:00:22 +0000144 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 */
148};
149
150/**
151 * xmlSchematronValidCtxt:
152 *
153 * A Schematrons validation context
154 */
155struct _xmlSchematronValidCtxt {
156 int type;
157 int flags; /* an or of xmlSchematronValidOptions */
158
159 xmlDictPtr dict;
160 int nberrors;
161 int err;
162
163 xmlSchematronPtr schema;
164 xmlXPathContextPtr xctxt;
165
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;
170 void *ioctx;
171};
172
173struct _xmlSchematronParserCtxt {
174 int type;
175 const xmlChar *URL;
176 xmlDocPtr doc;
177 int preserve; /* Whether the doc should be freed */
178 const char *buffer;
179 int size;
180
181 xmlDictPtr dict; /* dictionnary for interned string names */
182
183 int nberrors;
184 int err;
185 xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
186 xmlSchematronPtr schema;
187
188 int nbNamespaces; /* number of namespaces in the array */
189 int maxNamespaces; /* size of the array */
190 const xmlChar **namespaces; /* the array of namespaces */
191
192 int nbIncludes; /* number of includes in the array */
193 int maxIncludes; /* size of the array */
194 xmlNodePtr *includes; /* the array of includes */
195
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 */
201
202};
203
204#define XML_STRON_CTXT_PARSER 1
205#define XML_STRON_CTXT_VALIDATOR 2
206
207/************************************************************************
208 * *
209 * Error reporting *
210 * *
211 ************************************************************************/
212
213/**
214 * xmlSchematronPErrMemory:
215 * @node: a context node
216 * @extra: extra informations
217 *
218 * Handle an out of memory condition
219 */
220static void
221xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
222 const char *extra, xmlNodePtr node)
223{
224 if (ctxt != NULL)
225 ctxt->nberrors++;
226 __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
227 extra);
228}
229
230/**
231 * xmlSchematronPErr:
232 * @ctxt: the parsing context
233 * @node: the context node
234 * @error: the error code
235 * @msg: the error message
236 * @str1: extra data
237 * @str2: extra data
238 *
239 * Handle a parser error
240 */
241static void
242xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
243 const char *msg, const xmlChar * str1, const xmlChar * str2)
244{
245 xmlGenericErrorFunc channel = NULL;
246 xmlStructuredErrorFunc schannel = NULL;
247 void *data = NULL;
248
249 if (ctxt != NULL) {
250 ctxt->nberrors++;
251 channel = ctxt->error;
252 data = ctxt->userData;
253 schannel = ctxt->serror;
254 }
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,
258 msg, str1, str2);
259}
260
261/**
262 * xmlSchematronVTypeErrMemory:
263 * @node: a context node
264 * @extra: extra informations
265 *
266 * Handle an out of memory condition
267 */
268static void
269xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
270 const char *extra, xmlNodePtr node)
271{
272 if (ctxt != NULL) {
273 ctxt->nberrors++;
274 ctxt->err = XML_SCHEMAV_INTERNAL;
275 }
276 __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
277 extra);
278}
279
280/************************************************************************
281 * *
282 * Parsing and compilation of the Schematrontrons *
283 * *
284 ************************************************************************/
285
286/**
287 * xmlSchematronAddTest:
288 * @ctxt: the schema parsing context
Daniel Veillarde70375c2005-07-30 21:09:12 +0000289 * @type: the type of test
290 * @rule: the parent rule
Daniel Veillarded6c5492005-07-23 15:00:22 +0000291 * @node: the node hosting the test
Daniel Veillarde70375c2005-07-30 21:09:12 +0000292 * @test: the associated test
Daniel Veillardd4501d72005-07-24 14:27:16 +0000293 * @report: the associated report string
Daniel Veillarded6c5492005-07-23 15:00:22 +0000294 *
295 * Add a test to a schematron
296 *
297 * Returns the new pointer or NULL in case of error
298 */
299static xmlSchematronTestPtr
Daniel Veillarde70375c2005-07-30 21:09:12 +0000300xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
301 xmlSchematronTestType type,
Daniel Veillarded6c5492005-07-23 15:00:22 +0000302 xmlSchematronRulePtr rule,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000303 xmlNodePtr node, xmlChar *test, xmlChar *report)
Daniel Veillarded6c5492005-07-23 15:00:22 +0000304{
305 xmlSchematronTestPtr ret;
306 xmlXPathCompExprPtr comp;
307
308 if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
309 (test == NULL))
310 return(NULL);
311
312 /*
313 * try first to compile the test expression
314 */
315 comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
316 if (comp == NULL) {
317 xmlSchematronPErr(ctxt, node,
318 XML_SCHEMAP_NOROOT,
319 "Failed to compile test expression %s",
320 test, NULL);
321 return(NULL);
322 }
323
324 ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
325 if (ret == NULL) {
326 xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
327 return (NULL);
328 }
329 memset(ret, 0, sizeof(xmlSchematronTest));
330 ret->type = type;
331 ret->node = node;
332 ret->test = test;
333 ret->comp = comp;
Daniel Veillardd4501d72005-07-24 14:27:16 +0000334 ret->report = report;
Daniel Veillardc740a172005-07-31 12:17:24 +0000335 ret->next = NULL;
336 if (rule->tests == NULL) {
337 rule->tests = ret;
338 } else {
339 xmlSchematronTestPtr prev = rule->tests;
340
341 while (prev->next != NULL)
342 prev = prev->next;
343 prev->next = ret;
344 }
Daniel Veillarded6c5492005-07-23 15:00:22 +0000345 return (ret);
346}
347
348/**
349 * xmlSchematronFreeTests:
350 * @tests: a list of tests
351 *
352 * Free a list of tests.
353 */
354static void
355xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
356 xmlSchematronTestPtr next;
357
358 while (tests != NULL) {
359 next = tests->next;
360 if (tests->test != NULL)
361 xmlFree(tests->test);
362 if (tests->comp != NULL)
363 xmlXPathFreeCompExpr(tests->comp);
Daniel Veillardd4501d72005-07-24 14:27:16 +0000364 if (tests->report != NULL)
365 xmlFree(tests->report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000366 xmlFree(tests);
367 tests = next;
368 }
369}
370
371/**
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
Daniel Veillardd4501d72005-07-24 14:27:16 +0000377 * @report: the associated report string
Daniel Veillarded6c5492005-07-23 15:00:22 +0000378 *
379 * Add a rule to a schematron
380 *
381 * Returns the new pointer or NULL in case of error
382 */
383static xmlSchematronRulePtr
384xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
Daniel Veillarde70375c2005-07-30 21:09:12 +0000385 xmlSchematronPatternPtr pat, xmlNodePtr node,
386 xmlChar *context, xmlChar *report)
Daniel Veillarded6c5492005-07-23 15:00:22 +0000387{
388 xmlSchematronRulePtr ret;
389 xmlPatternPtr pattern;
390
391 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
392 (context == NULL))
393 return(NULL);
394
395 /*
396 * Try first to compile the pattern
397 */
398 pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
399 ctxt->namespaces);
400 if (pattern == NULL) {
401 xmlSchematronPErr(ctxt, node,
402 XML_SCHEMAP_NOROOT,
403 "Failed to compile context expression %s",
404 context, NULL);
405 }
406
407 ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
408 if (ret == NULL) {
409 xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
410 return (NULL);
411 }
412 memset(ret, 0, sizeof(xmlSchematronRule));
413 ret->node = node;
414 ret->context = context;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000415 ret->pattern = pattern;
Daniel Veillardd4501d72005-07-24 14:27:16 +0000416 ret->report = report;
Daniel Veillardc740a172005-07-31 12:17:24 +0000417 ret->next = NULL;
418 if (schema->rules == NULL) {
419 schema->rules = ret;
420 } else {
421 xmlSchematronRulePtr prev = schema->rules;
422
423 while (prev->next != NULL)
424 prev = prev->next;
425 prev->next = ret;
426 }
427 ret->patnext = NULL;
428 if (pat->rules == NULL) {
429 pat->rules = ret;
430 } else {
431 xmlSchematronRulePtr prev = pat->rules;
432
433 while (prev->patnext != NULL)
434 prev = prev->patnext;
435 prev->patnext = ret;
436 }
Daniel Veillarded6c5492005-07-23 15:00:22 +0000437 return (ret);
438}
439
440/**
441 * xmlSchematronFreeRules:
442 * @rules: a list of rules
443 *
444 * Free a list of rules.
445 */
446static void
447xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
448 xmlSchematronRulePtr next;
449
450 while (rules != NULL) {
451 next = rules->next;
452 if (rules->tests)
453 xmlSchematronFreeTests(rules->tests);
454 if (rules->context != NULL)
455 xmlFree(rules->context);
456 if (rules->pattern)
457 xmlFreePattern(rules->pattern);
Daniel Veillardd4501d72005-07-24 14:27:16 +0000458 if (rules->report != NULL)
459 xmlFree(rules->report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000460 xmlFree(rules);
461 rules = next;
462 }
463}
464
465/**
Daniel Veillarde70375c2005-07-30 21:09:12 +0000466 * 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
471 *
472 * Add a pattern to a schematron
473 *
474 * Returns the new pointer or NULL in case of error
475 */
476static xmlSchematronPatternPtr
477xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
478 xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
479{
480 xmlSchematronPatternPtr ret;
481
482 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
483 return(NULL);
484
485 ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
486 if (ret == NULL) {
487 xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
488 return (NULL);
489 }
490 memset(ret, 0, sizeof(xmlSchematronPattern));
491 ret->name = name;
Daniel Veillardc740a172005-07-31 12:17:24 +0000492 ret->next = NULL;
493 if (schema->patterns == NULL) {
494 schema->patterns = ret;
495 } else {
496 xmlSchematronPatternPtr prev = schema->patterns;
497
498 while (prev->next != NULL)
499 prev = prev->next;
500 prev->next = ret;
501 }
Daniel Veillarde70375c2005-07-30 21:09:12 +0000502 return (ret);
503}
504
505/**
506 * xmlSchematronFreePatterns:
507 * @patterns: a list of patterns
508 *
509 * Free a list of patterns.
510 */
511static void
512xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
513 xmlSchematronPatternPtr next;
514
515 while (patterns != NULL) {
516 next = patterns->next;
517 if (patterns->name != NULL)
518 xmlFree(patterns->name);
519 xmlFree(patterns);
520 patterns = next;
521 }
522}
523
524/**
Daniel Veillarded6c5492005-07-23 15:00:22 +0000525 * xmlSchematronNewSchematron:
526 * @ctxt: a schema validation context
527 *
528 * Allocate a new Schematron structure.
529 *
530 * Returns the newly allocated structure or NULL in case or error
531 */
532static xmlSchematronPtr
533xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
534{
535 xmlSchematronPtr ret;
536
537 ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
538 if (ret == NULL) {
539 xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
540 return (NULL);
541 }
542 memset(ret, 0, sizeof(xmlSchematron));
543 ret->dict = ctxt->dict;
544 xmlDictReference(ret->dict);
545
546 return (ret);
547}
548
549/**
550 * xmlSchematronFree:
551 * @schema: a schema structure
552 *
553 * Deallocate a Schematron structure.
554 */
555void
556xmlSchematronFree(xmlSchematronPtr schema)
557{
558 if (schema == NULL)
559 return;
560
561 if ((schema->doc != NULL) && (!(schema->preserve)))
562 xmlFreeDoc(schema->doc);
563
564 if (schema->namespaces != NULL)
Rob Richards54a8f672005-10-07 02:33:00 +0000565 xmlFree((char **) schema->namespaces);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000566
567 xmlSchematronFreeRules(schema->rules);
Daniel Veillarde70375c2005-07-30 21:09:12 +0000568 xmlSchematronFreePatterns(schema->patterns);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000569 xmlDictFree(schema->dict);
570 xmlFree(schema);
571}
572
573/**
574 * xmlSchematronNewParserCtxt:
575 * @URL: the location of the schema
576 *
577 * Create an XML Schematrons parse context for that file/resource expected
578 * to contain an XML Schematrons file.
579 *
580 * Returns the parser context or NULL in case of error
581 */
582xmlSchematronParserCtxtPtr
583xmlSchematronNewParserCtxt(const char *URL)
584{
585 xmlSchematronParserCtxtPtr ret;
586
587 if (URL == NULL)
588 return (NULL);
589
590 ret =
591 (xmlSchematronParserCtxtPtr)
592 xmlMalloc(sizeof(xmlSchematronParserCtxt));
593 if (ret == NULL) {
594 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
595 NULL);
596 return (NULL);
597 }
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);
Daniel Veillard24505b02005-07-28 23:49:35 +0000602 ret->includes = NULL;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000603 ret->xctxt = xmlXPathNewContext(NULL);
604 if (ret->xctxt == NULL) {
605 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
606 NULL);
607 xmlSchematronFreeParserCtxt(ret);
608 return (NULL);
609 }
610 ret->xctxt->flags = XML_XPATH_CHECKNS;
611 return (ret);
612}
613
614/**
615 * xmlSchematronNewMemParserCtxt:
616 * @buffer: a pointer to a char array containing the schemas
617 * @size: the size of the array
618 *
619 * Create an XML Schematrons parse context for that memory buffer expected
620 * to contain an XML Schematrons file.
621 *
622 * Returns the parser context or NULL in case of error
623 */
624xmlSchematronParserCtxtPtr
625xmlSchematronNewMemParserCtxt(const char *buffer, int size)
626{
627 xmlSchematronParserCtxtPtr ret;
628
629 if ((buffer == NULL) || (size <= 0))
630 return (NULL);
631
632 ret =
633 (xmlSchematronParserCtxtPtr)
634 xmlMalloc(sizeof(xmlSchematronParserCtxt));
635 if (ret == NULL) {
636 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
637 NULL);
638 return (NULL);
639 }
640 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
641 ret->buffer = buffer;
642 ret->size = size;
643 ret->dict = xmlDictCreate();
644 ret->xctxt = xmlXPathNewContext(NULL);
645 if (ret->xctxt == NULL) {
646 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
647 NULL);
648 xmlSchematronFreeParserCtxt(ret);
649 return (NULL);
650 }
651 return (ret);
652}
653
654/**
655 * xmlSchematronNewDocParserCtxt:
656 * @doc: a preparsed document tree
657 *
658 * Create an XML Schematrons parse context for that document.
659 * NB. The document may be modified during the parsing process.
660 *
661 * Returns the parser context or NULL in case of error
662 */
663xmlSchematronParserCtxtPtr
664xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
665{
666 xmlSchematronParserCtxtPtr ret;
667
668 if (doc == NULL)
669 return (NULL);
670
671 ret =
672 (xmlSchematronParserCtxtPtr)
673 xmlMalloc(sizeof(xmlSchematronParserCtxt));
674 if (ret == NULL) {
675 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
676 NULL);
677 return (NULL);
678 }
679 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
680 ret->doc = doc;
681 ret->dict = xmlDictCreate();
682 /* The application has responsibility for the document */
683 ret->preserve = 1;
684 ret->xctxt = xmlXPathNewContext(doc);
685 if (ret->xctxt == NULL) {
686 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
687 NULL);
688 xmlSchematronFreeParserCtxt(ret);
689 return (NULL);
690 }
691
692 return (ret);
693}
694
695/**
696 * xmlSchematronFreeParserCtxt:
697 * @ctxt: the schema parser context
698 *
699 * Free the resources associated to the schema parser context
700 */
701void
702xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
703{
704 if (ctxt == NULL)
705 return;
706 if (ctxt->doc != NULL && !ctxt->preserve)
707 xmlFreeDoc(ctxt->doc);
708 if (ctxt->xctxt != NULL) {
709 xmlXPathFreeContext(ctxt->xctxt);
710 }
711 if (ctxt->namespaces != NULL)
Rob Richards54a8f672005-10-07 02:33:00 +0000712 xmlFree((char **) ctxt->namespaces);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000713 xmlDictFree(ctxt->dict);
714 xmlFree(ctxt);
715}
716
717/**
718 * xmlSchematronPushInclude:
719 * @ctxt: the schema parser context
720 * @doc: the included document
721 * @cur: the current include node
722 *
723 * Add an included document
724 */
725static void
726xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
727 xmlDocPtr doc, xmlNodePtr cur)
728{
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",
735 NULL);
736 return;
737 }
738 ctxt->nbIncludes = 0;
739 } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
740 xmlNodePtr *tmp;
741
742 tmp = (xmlNodePtr *)
743 xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
744 sizeof(xmlNodePtr));
745 if (tmp == NULL) {
746 xmlSchematronPErrMemory(NULL, "allocating parser includes",
747 NULL);
748 return;
749 }
750 ctxt->includes = tmp;
751 ctxt->maxIncludes *= 2;
752 }
753 ctxt->includes[2 * ctxt->nbIncludes] = cur;
754 ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
755 ctxt->nbIncludes++;
756}
757
758/**
759 * xmlSchematronPopInclude:
760 * @ctxt: the schema parser context
761 *
762 * Pop an include level. The included document is being freed
763 *
764 * Returns the node immediately following the include or NULL if the
765 * include list was empty.
766 */
767static xmlNodePtr
768xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
769{
770 xmlDocPtr doc;
771 xmlNodePtr ret;
772
773 if (ctxt->nbIncludes <= 0)
774 return(NULL);
775 ctxt->nbIncludes--;
776 doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
777 ret = ctxt->includes[2 * ctxt->nbIncludes];
778 xmlFreeDoc(doc);
779 if (ret != NULL)
780 ret = ret->next;
781 if (ret == NULL)
782 return(xmlSchematronPopInclude(ctxt));
783 return(ret);
784}
785
786/**
787 * xmlSchematronAddNamespace:
788 * @ctxt: the schema parser context
789 * @prefix: the namespace prefix
790 * @ns: the namespace name
791 *
792 * Add a namespace definition in the context
793 */
794static void
795xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
796 const xmlChar *prefix, const xmlChar *ns)
797{
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",
804 NULL);
805 return;
806 }
807 ctxt->nbNamespaces = 0;
808 } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
809 const xmlChar **tmp;
810
811 tmp = (const xmlChar **)
Rob Richards54a8f672005-10-07 02:33:00 +0000812 xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
Daniel Veillarded6c5492005-07-23 15:00:22 +0000813 sizeof(const xmlChar *));
814 if (tmp == NULL) {
815 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
816 NULL);
817 return;
818 }
819 ctxt->namespaces = tmp;
820 ctxt->maxNamespaces *= 2;
821 }
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;
829
830}
831
832/**
833 * xmlSchematronParseRule:
834 * @ctxt: a schema validation context
835 * @rule: the rule node
836 *
837 * parse a rule element
838 */
839static void
Daniel Veillarde70375c2005-07-30 21:09:12 +0000840xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
841 xmlSchematronPatternPtr pattern,
842 xmlNodePtr rule)
Daniel Veillarded6c5492005-07-23 15:00:22 +0000843{
844 xmlNodePtr cur;
845 int nbChecks = 0;
846 xmlChar *test;
847 xmlChar *context;
Daniel Veillardd4501d72005-07-24 14:27:16 +0000848 xmlChar *report;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000849 xmlSchematronRulePtr ruleptr;
850 xmlSchematronTestPtr testptr;
851
852 if ((ctxt == NULL) || (rule == NULL)) return;
853
854 context = xmlGetNoNsProp(rule, BAD_CAST "context");
855 if (context == NULL) {
856 xmlSchematronPErr(ctxt, rule,
857 XML_SCHEMAP_NOROOT,
858 "rule has no context attribute",
859 NULL, NULL);
860 return;
861 } else if (context[0] == 0) {
862 xmlSchematronPErr(ctxt, rule,
863 XML_SCHEMAP_NOROOT,
864 "rule has an empty context attribute",
865 NULL, NULL);
866 xmlFree(context);
867 return;
868 } else {
Daniel Veillarde70375c2005-07-30 21:09:12 +0000869 ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
870 rule, context, NULL);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000871 if (ruleptr == NULL) {
872 xmlFree(context);
873 return;
874 }
875 }
876
877 cur = rule->children;
878 NEXT_SCHEMATRON(cur);
879 while (cur != NULL) {
880 if (IS_SCHEMATRON(cur, "assert")) {
881 nbChecks++;
882 test = xmlGetNoNsProp(cur, BAD_CAST "test");
883 if (test == NULL) {
884 xmlSchematronPErr(ctxt, cur,
885 XML_SCHEMAP_NOROOT,
886 "assert has no test attribute",
887 NULL, NULL);
888 } else if (test[0] == 0) {
889 xmlSchematronPErr(ctxt, cur,
890 XML_SCHEMAP_NOROOT,
891 "assert has an empty test attribute",
892 NULL, NULL);
893 xmlFree(test);
894 } else {
Daniel Veillardd4501d72005-07-24 14:27:16 +0000895 /* TODO will need dynamic processing instead */
896 report = xmlNodeGetContent(cur);
897
Daniel Veillarded6c5492005-07-23 15:00:22 +0000898 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000899 ruleptr, cur, test, report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000900 if (testptr == NULL)
901 xmlFree(test);
902 }
903 } else if (IS_SCHEMATRON(cur, "report")) {
904 nbChecks++;
905 test = xmlGetNoNsProp(cur, BAD_CAST "test");
906 if (test == NULL) {
907 xmlSchematronPErr(ctxt, cur,
908 XML_SCHEMAP_NOROOT,
909 "assert has no test attribute",
910 NULL, NULL);
911 } else if (test[0] == 0) {
912 xmlSchematronPErr(ctxt, cur,
913 XML_SCHEMAP_NOROOT,
914 "assert has an empty test attribute",
915 NULL, NULL);
916 xmlFree(test);
917 } else {
Daniel Veillardd4501d72005-07-24 14:27:16 +0000918 /* TODO will need dynamic processing instead */
919 report = xmlNodeGetContent(cur);
920
Daniel Veillarded6c5492005-07-23 15:00:22 +0000921 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000922 ruleptr, cur, test, report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000923 if (testptr == NULL)
924 xmlFree(test);
925 }
926 } else {
927 xmlSchematronPErr(ctxt, cur,
928 XML_SCHEMAP_NOROOT,
929 "Expecting an assert or a report element instead of %s",
930 cur->name, NULL);
931 }
932 cur = cur->next;
933 NEXT_SCHEMATRON(cur);
934 }
935 if (nbChecks == 0) {
936 xmlSchematronPErr(ctxt, rule,
937 XML_SCHEMAP_NOROOT,
938 "rule has no assert nor report element", NULL, NULL);
939 }
940}
941
942/**
943 * xmlSchematronParsePattern:
944 * @ctxt: a schema validation context
945 * @pat: the pattern node
946 *
947 * parse a pattern element
948 */
949static void
950xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
951{
952 xmlNodePtr cur;
Daniel Veillarde70375c2005-07-30 21:09:12 +0000953 xmlSchematronPatternPtr pattern;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000954 int nbRules = 0;
Daniel Veillarde70375c2005-07-30 21:09:12 +0000955 xmlChar *id;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000956
957 if ((ctxt == NULL) || (pat == NULL)) return;
958
Daniel Veillarde70375c2005-07-30 21:09:12 +0000959 id = xmlGetNoNsProp(pat, BAD_CAST "id");
960 if (id == NULL) {
961 id = xmlGetNoNsProp(pat, BAD_CAST "name");
962 }
963 pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
964 if (pattern == NULL) {
965 if (id != NULL)
966 xmlFree(id);
967 return;
968 }
Daniel Veillarded6c5492005-07-23 15:00:22 +0000969 cur = pat->children;
970 NEXT_SCHEMATRON(cur);
971 while (cur != NULL) {
972 if (IS_SCHEMATRON(cur, "rule")) {
Daniel Veillarde70375c2005-07-30 21:09:12 +0000973 xmlSchematronParseRule(ctxt, pattern, cur);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000974 nbRules++;
975 } else {
976 xmlSchematronPErr(ctxt, cur,
977 XML_SCHEMAP_NOROOT,
978 "Expecting a rule element instead of %s", cur->name, NULL);
979 }
980 cur = cur->next;
981 NEXT_SCHEMATRON(cur);
982 }
983 if (nbRules == 0) {
984 xmlSchematronPErr(ctxt, pat,
985 XML_SCHEMAP_NOROOT,
986 "Pattern has no rule element", NULL, NULL);
987 }
988}
989
990/**
991 * xmlSchematronLoadInclude:
992 * @ctxt: a schema validation context
993 * @cur: the include element
994 *
995 * Load the include document, Push the current pointer
996 *
997 * Returns the updated node pointer
998 */
999static xmlNodePtr
1000xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1001{
1002 xmlNodePtr ret = NULL;
1003 xmlDocPtr doc = NULL;
1004 xmlChar *href = NULL;
1005 xmlChar *base = NULL;
1006 xmlChar *URI = NULL;
1007
1008 if ((ctxt == NULL) || (cur == NULL))
1009 return(NULL);
1010
1011 href = xmlGetNoNsProp(cur, BAD_CAST "href");
1012 if (href == NULL) {
1013 xmlSchematronPErr(ctxt, cur,
1014 XML_SCHEMAP_NOROOT,
1015 "Include has no href attribute", NULL, NULL);
1016 return(cur->next);
1017 }
1018
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);
1023 if (doc == NULL) {
1024 xmlSchematronPErr(ctxt, cur,
1025 XML_SCHEMAP_FAILED_LOAD,
1026 "could not load include '%s'.\n",
1027 URI, NULL);
1028 goto done;
1029 }
1030 ret = xmlDocGetRootElement(doc);
1031 if (ret == NULL) {
1032 xmlSchematronPErr(ctxt, cur,
1033 XML_SCHEMAP_FAILED_LOAD,
1034 "could not find root from include '%s'.\n",
1035 URI, NULL);
1036 goto done;
1037 }
1038
1039 /* Success, push the include for rollback on exit */
1040 xmlSchematronPushInclude(ctxt, doc, cur);
1041
1042done:
1043 if (ret == NULL) {
1044 if (doc != NULL)
1045 xmlFreeDoc(doc);
1046 }
Daniel Veillard11ce4002006-03-10 00:36:23 +00001047 xmlFree(href);
1048 if (base != NULL)
Daniel Veillarded6c5492005-07-23 15:00:22 +00001049 xmlFree(base);
Daniel Veillard11ce4002006-03-10 00:36:23 +00001050 if (URI != NULL)
Daniel Veillarded6c5492005-07-23 15:00:22 +00001051 xmlFree(URI);
1052 return(ret);
1053}
1054
1055/**
1056 * xmlSchematronParse:
1057 * @ctxt: a schema validation context
1058 *
1059 * parse a schema definition resource and build an internal
1060 * XML Shema struture which can be used to validate instances.
1061 *
1062 * Returns the internal XML Schematron structure built from the resource or
1063 * NULL in case of error
1064 */
1065xmlSchematronPtr
1066xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1067{
1068 xmlSchematronPtr ret = NULL;
1069 xmlDocPtr doc;
1070 xmlNodePtr root, cur;
1071 int preserve = 0;
1072
1073 if (ctxt == NULL)
1074 return (NULL);
1075
1076 ctxt->nberrors = 0;
1077
1078 /*
1079 * First step is to parse the input document into an DOM/Infoset
1080 */
1081 if (ctxt->URL != NULL) {
1082 doc = xmlReadFile((const char *) ctxt->URL, NULL,
1083 SCHEMATRON_PARSE_OPTIONS);
1084 if (doc == NULL) {
1085 xmlSchematronPErr(ctxt, NULL,
1086 XML_SCHEMAP_FAILED_LOAD,
1087 "xmlSchematronParse: could not load '%s'.\n",
1088 ctxt->URL, NULL);
1089 return (NULL);
1090 }
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001091 ctxt->preserve = 0;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001092 } else if (ctxt->buffer != NULL) {
1093 doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1094 SCHEMATRON_PARSE_OPTIONS);
1095 if (doc == NULL) {
1096 xmlSchematronPErr(ctxt, NULL,
1097 XML_SCHEMAP_FAILED_PARSE,
1098 "xmlSchematronParse: could not parse.\n",
1099 NULL, NULL);
1100 return (NULL);
1101 }
1102 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1103 ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001104 ctxt->preserve = 0;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001105 } else if (ctxt->doc != NULL) {
1106 doc = ctxt->doc;
1107 preserve = 1;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001108 ctxt->preserve = 1;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001109 } else {
1110 xmlSchematronPErr(ctxt, NULL,
1111 XML_SCHEMAP_NOTHING_TO_PARSE,
1112 "xmlSchematronParse: could not parse.\n",
1113 NULL, NULL);
1114 return (NULL);
1115 }
1116
1117 /*
1118 * Then extract the root and Schematron parse it
1119 */
1120 root = xmlDocGetRootElement(doc);
1121 if (root == NULL) {
1122 xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1123 XML_SCHEMAP_NOROOT,
1124 "The schema has no document element.\n", NULL, NULL);
1125 if (!preserve) {
1126 xmlFreeDoc(doc);
1127 }
1128 return (NULL);
1129 }
1130
1131 if (!IS_SCHEMATRON(root, "schema")) {
1132 xmlSchematronPErr(ctxt, root,
1133 XML_SCHEMAP_NOROOT,
1134 "The XML document '%s' is not a XML schematron document",
1135 ctxt->URL, NULL);
1136 goto exit;
1137 }
1138 ret = xmlSchematronNewSchematron(ctxt);
1139 if (ret == NULL)
1140 goto exit;
1141 ctxt->schema = ret;
1142
1143 /*
1144 * scan the schema elements
1145 */
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);
1152 xmlFree(title);
1153 }
1154 cur = cur->next;
1155 NEXT_SCHEMATRON(cur);
1156 }
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,
1162 XML_SCHEMAP_NOROOT,
1163 "ns element has no uri", NULL, NULL);
1164 }
1165 if ((prefix == NULL) || (prefix[0] == 0)) {
1166 xmlSchematronPErr(ctxt, cur,
1167 XML_SCHEMAP_NOROOT,
1168 "ns element has no prefix", NULL, NULL);
1169 }
1170 if ((prefix) && (uri)) {
1171 xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1172 xmlSchematronAddNamespace(ctxt, prefix, uri);
1173 ret->nbNs++;
1174 }
1175 if (uri)
1176 xmlFree(uri);
1177 if (prefix)
1178 xmlFree(prefix);
1179 cur = cur->next;
1180 NEXT_SCHEMATRON(cur);
1181 }
1182 while (cur != NULL) {
1183 if (IS_SCHEMATRON(cur, "pattern")) {
1184 xmlSchematronParsePattern(ctxt, cur);
1185 ret->nbPattern++;
1186 } else {
1187 xmlSchematronPErr(ctxt, cur,
1188 XML_SCHEMAP_NOROOT,
1189 "Expecting a pattern element instead of %s", cur->name, NULL);
1190 }
1191 cur = cur->next;
1192 NEXT_SCHEMATRON(cur);
1193 }
1194 if (ret->nbPattern == 0) {
1195 xmlSchematronPErr(ctxt, root,
1196 XML_SCHEMAP_NOROOT,
1197 "The schematron document '%s' has no pattern",
1198 ctxt->URL, NULL);
1199 goto exit;
1200 }
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001201 /* the original document must be kept for reporting */
1202 ret->doc = doc;
1203 preserve = 1;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001204
1205exit:
1206 if (!preserve) {
1207 xmlFreeDoc(doc);
1208 }
Daniel Veillard11ce4002006-03-10 00:36:23 +00001209 if (ret != NULL) {
1210 if (ctxt->nberrors != 0) {
1211 xmlSchematronFree(ret);
1212 ret = NULL;
1213 } else {
1214 ret->namespaces = ctxt->namespaces;
1215 ret->nbNamespaces = ctxt->nbNamespaces;
1216 ctxt->namespaces = NULL;
1217 }
Daniel Veillarded6c5492005-07-23 15:00:22 +00001218 }
1219 return (ret);
1220}
1221
1222/************************************************************************
1223 * *
1224 * Schematrontron Reports handler *
1225 * *
1226 ************************************************************************/
1227
Daniel Veillardd541c8f2005-07-31 16:49:51 +00001228static xmlNodePtr
1229xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1230 xmlNodePtr cur, const xmlChar *xpath) {
1231 xmlNodePtr node = NULL;
1232 xmlXPathObjectPtr ret;
1233
1234 if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1235 return(NULL);
1236
1237 ctxt->xctxt->doc = cur->doc;
1238 ctxt->xctxt->node = cur;
1239 ret = xmlXPathEval(xpath, ctxt->xctxt);
1240 if (ret == NULL)
1241 return(NULL);
1242
1243 if ((ret->type == XPATH_NODESET) &&
1244 (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1245 node = ret->nodesetval->nodeTab[0];
1246
1247 xmlXPathFreeObject(ret);
1248 return(node);
1249}
1250
Daniel Veillardd4501d72005-07-24 14:27:16 +00001251/**
1252 * xmlSchematronReportOutput:
1253 * @ctxt: the validation context
1254 * @cur: the current node tested
1255 * @msg: the message output
1256 *
1257 * Output part of the report to whatever channel the user selected
1258 */
1259static void
1260xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1261 xmlNodePtr cur ATTRIBUTE_UNUSED,
Daniel Veillardc740a172005-07-31 12:17:24 +00001262 const char *msg) {
Daniel Veillardd4501d72005-07-24 14:27:16 +00001263 /* TODO */
1264 fprintf(stderr, "%s", msg);
1265}
1266
1267/**
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001268 * xmlSchematronFormatReport:
1269 * @ctxt: the validation context
1270 * @test: the test node
1271 * @cur: the current node tested
1272 *
1273 * Build the string being reported to the user.
1274 *
1275 * Returns a report string or NULL in case of error. The string needs
1276 * to be deallocated by teh caller
1277 */
1278static xmlChar *
1279xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1280 xmlNodePtr test, xmlNodePtr cur) {
1281 xmlChar *ret = NULL;
Daniel Veillardd541c8f2005-07-31 16:49:51 +00001282 xmlNodePtr child, node;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001283
1284 if ((test == NULL) || (cur == NULL))
1285 return(ret);
1286
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")) {
Daniel Veillardd541c8f2005-07-31 16:49:51 +00001293 xmlChar *path;
1294
1295 path = xmlGetNoNsProp(child, BAD_CAST "path");
1296
1297 node = cur;
1298 if (path != NULL) {
1299 node = xmlSchematronGetNode(ctxt, cur, path);
1300 if (node == NULL)
1301 node = cur;
1302 xmlFree(path);
1303 }
1304
1305 if ((node->ns == NULL) || (node->ns->prefix == NULL))
1306 ret = xmlStrcat(ret, node->name);
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001307 else {
Daniel Veillardd541c8f2005-07-31 16:49:51 +00001308 ret = xmlStrcat(ret, node->ns->prefix);
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001309 ret = xmlStrcat(ret, BAD_CAST ":");
Daniel Veillardd541c8f2005-07-31 16:49:51 +00001310 ret = xmlStrcat(ret, node->name);
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001311 }
1312 } else {
1313 child = child->next;
1314 continue;
1315 }
1316
1317 /*
1318 * remove superfluous \n
1319 */
1320 if (ret != NULL) {
1321 int len = xmlStrlen(ret);
1322 xmlChar c;
1323
1324 if (len > 0) {
1325 c = ret[len - 1];
1326 if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1327 while ((c == ' ') || (c == '\n') ||
1328 (c == '\r') || (c == '\t')) {
1329 len--;
1330 if (len == 0)
1331 break;
1332 c = ret[len - 1];
1333 }
1334 ret[len] = ' ';
1335 ret[len + 1] = 0;
1336 }
1337 }
1338 }
1339
1340 child = child->next;
1341 }
1342 return(ret);
1343}
1344
1345/**
Daniel Veillardd4501d72005-07-24 14:27:16 +00001346 * xmlSchematronReportSuccess:
1347 * @ctxt: the validation context
1348 * @test: the compiled test
1349 * @cur: the current node tested
1350 * @success: boolean value for the result
1351 *
1352 * called from the validation engine when an assert or report test have
1353 * been done.
1354 */
Daniel Veillarded6c5492005-07-23 15:00:22 +00001355static void
1356xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
Daniel Veillardd4501d72005-07-24 14:27:16 +00001357 xmlSchematronTestPtr test, xmlNodePtr cur, int success) {
1358 if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1359 return;
1360 /* if quiet and not SVRL report only failures */
1361 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
Daniel Veillarde70375c2005-07-30 21:09:12 +00001362 ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
Daniel Veillardc740a172005-07-31 12:17:24 +00001363 (test->type == XML_SCHEMATRON_REPORT))
Daniel Veillardd4501d72005-07-24 14:27:16 +00001364 return;
1365 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1366 TODO
1367 } else {
1368 xmlChar *path;
1369 char msg[1000];
1370 long line;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001371 const xmlChar *report = NULL;
Daniel Veillardd4501d72005-07-24 14:27:16 +00001372
Daniel Veillarde70375c2005-07-30 21:09:12 +00001373 if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
1374 ((test->type == XML_SCHEMATRON_ASSERT) & (success)))
Daniel Veillardd4501d72005-07-24 14:27:16 +00001375 return;
1376 line = xmlGetLineNo(cur);
1377 path = xmlGetNodePath(cur);
1378 if (path == NULL)
1379 path = (xmlChar *) cur->name;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001380#if 0
Daniel Veillardd4501d72005-07-24 14:27:16 +00001381 if ((test->report != NULL) && (test->report[0] != 0))
1382 report = test->report;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001383#endif
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);
1390 } else {
1391 snprintf(msg, 999, "%s line %ld: node failed report\n",
1392 (const char *) path, line);
1393 }
Daniel Veillardd4501d72005-07-24 14:27:16 +00001394 } else {
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001395 snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1396 line, (const char *) report);
Daniel Veillardd541c8f2005-07-31 16:49:51 +00001397 xmlFree((char *) report);
Daniel Veillardd4501d72005-07-24 14:27:16 +00001398 }
Daniel Veillardd4501d72005-07-24 14:27:16 +00001399 xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1400 if ((path != NULL) && (path != (xmlChar *) cur->name))
1401 xmlFree(path);
1402 }
Daniel Veillarded6c5492005-07-23 15:00:22 +00001403}
1404
Daniel Veillardc740a172005-07-31 12:17:24 +00001405/**
1406 * xmlSchematronReportPattern:
1407 * @ctxt: the validation context
1408 * @pattern: the current pattern
1409 *
1410 * called from the validation engine when starting to check a pattern
1411 */
1412static void
1413xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1414 xmlSchematronPatternPtr pattern) {
1415 if ((ctxt == NULL) || (pattern == NULL))
1416 return;
1417 if (ctxt->flags & XML_SCHEMATRON_OUT_QUIET)
1418 return;
1419 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1420 TODO
1421 } else {
1422 char msg[1000];
1423
1424 if (pattern->name == NULL)
1425 return;
1426 snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1427 xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1428 }
1429}
1430
1431
Daniel Veillarded6c5492005-07-23 15:00:22 +00001432/************************************************************************
1433 * *
1434 * Validation against a Schematrontron *
1435 * *
1436 ************************************************************************/
1437
1438/**
1439 * xmlSchematronNewValidCtxt:
1440 * @schema: a precompiled XML Schematrons
1441 * @options: a set of xmlSchematronValidOptions
1442 *
1443 * Create an XML Schematrons validation context based on the given schema.
1444 *
1445 * Returns the validation context or NULL in case of error
1446 */
1447xmlSchematronValidCtxtPtr
1448xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1449{
1450 int i;
1451 xmlSchematronValidCtxtPtr ret;
1452
Daniel Veillardc740a172005-07-31 12:17:24 +00001453 ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
Daniel Veillarded6c5492005-07-23 15:00:22 +00001454 if (ret == NULL) {
1455 xmlSchematronVErrMemory(NULL, "allocating validation context",
1456 NULL);
1457 return (NULL);
1458 }
1459 memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1460 ret->type = XML_STRON_CTXT_VALIDATOR;
1461 ret->schema = schema;
1462 ret->xctxt = xmlXPathNewContext(NULL);
Daniel Veillardd4501d72005-07-24 14:27:16 +00001463 ret->flags = options;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001464 if (ret->xctxt == NULL) {
1465 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
1466 NULL);
1467 xmlSchematronFreeValidCtxt(ret);
1468 return (NULL);
1469 }
1470 for (i = 0;i < schema->nbNamespaces;i++) {
1471 if ((schema->namespaces[2 * i] == NULL) ||
1472 (schema->namespaces[2 * i + 1] == NULL))
1473 break;
1474 xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1475 schema->namespaces[2 * i]);
1476 }
1477 return (ret);
1478}
1479
1480/**
1481 * xmlSchematronFreeValidCtxt:
1482 * @ctxt: the schema validation context
1483 *
1484 * Free the resources associated to the schema validation context
1485 */
1486void
1487xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1488{
1489 if (ctxt == NULL)
1490 return;
1491 if (ctxt->xctxt != NULL)
1492 xmlXPathFreeContext(ctxt->xctxt);
1493 if (ctxt->dict != NULL)
1494 xmlDictFree(ctxt->dict);
1495 xmlFree(ctxt);
1496}
1497
1498static xmlNodePtr
1499xmlSchematronNextNode(xmlNodePtr cur) {
1500 if (cur->children != NULL) {
1501 /*
1502 * Do not descend on entities declarations
1503 */
1504 if (cur->children->type != XML_ENTITY_DECL) {
1505 cur = cur->children;
1506 /*
1507 * Skip DTDs
1508 */
1509 if (cur->type != XML_DTD_NODE)
1510 return(cur);
1511 }
1512 }
1513
1514 while (cur->next != NULL) {
1515 cur = cur->next;
1516 if ((cur->type != XML_ENTITY_DECL) &&
1517 (cur->type != XML_DTD_NODE))
1518 return(cur);
1519 }
1520
1521 do {
1522 cur = cur->parent;
Daniel Veillard11ce4002006-03-10 00:36:23 +00001523 if (cur == NULL) break;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001524 if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1525 if (cur->next != NULL) {
1526 cur = cur->next;
1527 return(cur);
1528 }
1529 } while (cur != NULL);
1530 return(cur);
1531}
1532
1533/**
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
1539 *
1540 * Validate a rule against a tree instance at a given position
1541 *
1542 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1543 */
1544static int
1545xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1546 xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur)
1547{
1548 xmlXPathObjectPtr ret;
1549 int failed;
1550
1551 failed = 0;
1552 ctxt->xctxt->doc = instance;
1553 ctxt->xctxt->node = cur;
1554 ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1555 if (ret == NULL) {
1556 failed = 1;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001557 } else {
1558 switch (ret->type) {
1559 case XPATH_XSLT_TREE:
1560 case XPATH_NODESET:
1561 if ((ret->nodesetval == NULL) ||
1562 (ret->nodesetval->nodeNr == 0))
1563 failed = 1;
1564 break;
1565 case XPATH_BOOLEAN:
1566 failed = !ret->boolval;
1567 break;
1568 case XPATH_NUMBER:
1569 if ((xmlXPathIsNaN(ret->floatval)) ||
1570 (ret->floatval == 0.0))
1571 failed = 1;
1572 break;
1573 case XPATH_STRING:
1574 if ((ret->stringval == NULL) ||
1575 (ret->stringval[0] == 0))
1576 failed = 1;
1577 break;
1578 case XPATH_UNDEFINED:
1579 case XPATH_POINT:
1580 case XPATH_RANGE:
1581 case XPATH_LOCATIONSET:
1582 case XPATH_USERS:
Daniel Veillarded6c5492005-07-23 15:00:22 +00001583 failed = 1;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001584 break;
1585 }
1586 xmlXPathFreeObject(ret);
Daniel Veillarded6c5492005-07-23 15:00:22 +00001587 }
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001588 if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
Daniel Veillardd4501d72005-07-24 14:27:16 +00001589 ctxt->nberrors++;
Daniel Veillardeaecb3e2005-07-31 13:43:14 +00001590 else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1591 ctxt->nberrors++;
1592
Daniel Veillardd4501d72005-07-24 14:27:16 +00001593 xmlSchematronReportSuccess(ctxt, test, cur, !failed);
Daniel Veillarded6c5492005-07-23 15:00:22 +00001594
1595 return(!failed);
1596}
1597
1598/**
1599 * xmlSchematronValidateDoc:
1600 * @ctxt: the schema validation context
1601 * @instance: the document instace tree
1602 *
1603 * Validate a tree instance against the schematron
1604 *
1605 * Returns 0 in case of success, -1 in case of internal error
1606 * and an error count otherwise.
1607 */
1608int
1609xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1610{
Daniel Veillarde70375c2005-07-30 21:09:12 +00001611 xmlNodePtr cur, root;
1612 xmlSchematronPatternPtr pattern;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001613 xmlSchematronRulePtr rule;
1614 xmlSchematronTestPtr test;
1615
1616 if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1617 (ctxt->schema->rules == NULL) || (instance == NULL))
1618 return(-1);
1619 ctxt->nberrors = 0;
Daniel Veillarde70375c2005-07-30 21:09:12 +00001620 root = xmlDocGetRootElement(instance);
1621 if (root == NULL) {
1622 TODO
1623 ctxt->nberrors++;
1624 return(1);
1625 }
1626 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1627 (ctxt->flags == 0)) {
1628 /*
1629 * we are just trying to assert the validity of the document,
1630 * speed primes over the output, run in a single pass
1631 */
1632 cur = root;
1633 while (cur != NULL) {
1634 rule = ctxt->schema->rules;
1635 while (rule != NULL) {
1636 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1637 test = rule->tests;
1638 while (test != NULL) {
1639 xmlSchematronRunTest(ctxt, test, instance, cur);
1640 test = test->next;
1641 }
Daniel Veillarded6c5492005-07-23 15:00:22 +00001642 }
Daniel Veillarde70375c2005-07-30 21:09:12 +00001643 rule = rule->next;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001644 }
Daniel Veillarde70375c2005-07-30 21:09:12 +00001645
1646 cur = xmlSchematronNextNode(cur);
Daniel Veillarded6c5492005-07-23 15:00:22 +00001647 }
Daniel Veillarde70375c2005-07-30 21:09:12 +00001648 } else {
1649 /*
1650 * Process all contexts one at a time
1651 */
1652 pattern = ctxt->schema->patterns;
1653
1654 while (pattern != NULL) {
Daniel Veillardc740a172005-07-31 12:17:24 +00001655 xmlSchematronReportPattern(ctxt, pattern);
1656
Daniel Veillarde70375c2005-07-30 21:09:12 +00001657 /*
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
1662 */
1663 cur = root;
1664 while (cur != NULL) {
1665 rule = pattern->rules;
1666 while (rule != NULL) {
1667 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1668 test = rule->tests;
1669 while (test != NULL) {
1670 xmlSchematronRunTest(ctxt, test, instance, cur);
1671 test = test->next;
1672 }
1673 }
1674 rule = rule->patnext;
1675 }
1676
1677 cur = xmlSchematronNextNode(cur);
1678 }
Daniel Veillardc740a172005-07-31 12:17:24 +00001679 pattern = pattern->next;
Daniel Veillarde70375c2005-07-30 21:09:12 +00001680 }
Daniel Veillarded6c5492005-07-23 15:00:22 +00001681 }
1682 return(ctxt->nberrors);
1683}
1684
1685#ifdef STANDALONE
1686int
1687main(void)
1688{
1689 int ret;
1690 xmlDocPtr instance;
1691 xmlSchematronParserCtxtPtr pctxt;
1692 xmlSchematronValidCtxtPtr vctxt;
1693 xmlSchematronPtr schema = NULL;
1694
1695 pctxt = xmlSchematronNewParserCtxt("tst.sct");
1696 if (pctxt == NULL) {
1697 fprintf(stderr, "failed to build schematron parser\n");
1698 } else {
1699 schema = xmlSchematronParse(pctxt);
1700 if (schema == NULL) {
1701 fprintf(stderr, "failed to compile schematron\n");
1702 }
1703 xmlSchematronFreeParserCtxt(pctxt);
1704 }
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");
1709 }
1710 if ((schema != NULL) && (instance != NULL)) {
1711 vctxt = xmlSchematronNewValidCtxt(schema);
1712 if (vctxt == NULL) {
1713 fprintf(stderr, "failed to build schematron validator\n");
1714 } else {
1715 ret = xmlSchematronValidateDoc(vctxt, instance);
1716 xmlSchematronFreeValidCtxt(vctxt);
1717 }
1718 }
1719 xmlSchematronFree(schema);
1720 xmlFreeDoc(instance);
1721
1722 xmlCleanupParser();
1723 xmlMemoryDump();
1724
1725 return (0);
1726}
1727#endif
Daniel Veillardccb4d412005-08-23 13:41:17 +00001728#define bottom_schematron
1729#include "elfgcchack.h"
Daniel Veillarded6c5492005-07-23 15:00:22 +00001730#endif /* LIBXML_SCHEMATRON_ENABLED */