VirtualBox

source: vbox/trunk/src/libs/libxml2-2.12.6/schematron.c

Last change on this file was 104106, checked in by vboxsync, 8 weeks ago

libxml2-2.9.14: Applied and adjusted our libxml2 changes to 2.9.14. bugref:10640

  • Property svn:eol-style set to native
File size: 61.3 KB
Line 
1/*
2 * schematron.c : implementation of the Schematron schema validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * 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
21 */
22
23#define IN_LIBXML
24#include "libxml.h"
25
26#ifdef LIBXML_SCHEMATRON_ENABLED
27
28#include <stdlib.h>
29#include <string.h>
30#include <libxml/parser.h>
31#include <libxml/tree.h>
32#include <libxml/uri.h>
33#include <libxml/xpath.h>
34#include <libxml/xpathInternals.h>
35#include <libxml/pattern.h>
36#include <libxml/schematron.h>
37
38#include "private/error.h"
39
40#define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
41
42#define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
43
44#define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
45
46
47static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
48static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
49
50#define IS_SCHEMATRON(node, elem) \
51 ((node != NULL) && (node->type == XML_ELEMENT_NODE ) && \
52 (node->ns != NULL) && \
53 (xmlStrEqual(node->name, (const xmlChar *) elem)) && \
54 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
55 (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
56
57#define NEXT_SCHEMATRON(node) \
58 while (node != NULL) { \
59 if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \
60 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
61 (xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) \
62 break; \
63 node = node->next; \
64 }
65
66/**
67 * TODO:
68 *
69 * macro to flag unimplemented blocks
70 */
71#define TODO \
72 xmlGenericError(xmlGenericErrorContext, \
73 "Unimplemented block at %s:%d\n", \
74 __FILE__, __LINE__);
75
76typedef enum {
77 XML_SCHEMATRON_ASSERT=1,
78 XML_SCHEMATRON_REPORT=2
79} xmlSchematronTestType;
80
81/**
82 * _xmlSchematronLet:
83 *
84 * A Schematron let variable
85 */
86typedef struct _xmlSchematronLet xmlSchematronLet;
87typedef xmlSchematronLet *xmlSchematronLetPtr;
88struct _xmlSchematronLet {
89 xmlSchematronLetPtr next; /* the next let variable in the list */
90 xmlChar *name; /* the name of the variable */
91 xmlXPathCompExprPtr comp; /* the compiled expression */
92};
93
94/**
95 * _xmlSchematronTest:
96 *
97 * A Schematrons test, either an assert or a report
98 */
99typedef struct _xmlSchematronTest xmlSchematronTest;
100typedef xmlSchematronTest *xmlSchematronTestPtr;
101struct _xmlSchematronTest {
102 xmlSchematronTestPtr next; /* the next test in the list */
103 xmlSchematronTestType type; /* the test type */
104 xmlNodePtr node; /* the node in the tree */
105 xmlChar *test; /* the expression to test */
106 xmlXPathCompExprPtr comp; /* the compiled expression */
107 xmlChar *report; /* the message to report */
108};
109
110/**
111 * _xmlSchematronRule:
112 *
113 * A Schematrons rule
114 */
115typedef struct _xmlSchematronRule xmlSchematronRule;
116typedef xmlSchematronRule *xmlSchematronRulePtr;
117struct _xmlSchematronRule {
118 xmlSchematronRulePtr next; /* the next rule in the list */
119 xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
120 xmlNodePtr node; /* the node in the tree */
121 xmlChar *context; /* the context evaluation rule */
122 xmlSchematronTestPtr tests; /* the list of tests */
123 xmlPatternPtr pattern; /* the compiled pattern associated */
124 xmlChar *report; /* the message to report */
125 xmlSchematronLetPtr lets; /* the list of let variables */
126};
127
128/**
129 * _xmlSchematronPattern:
130 *
131 * A Schematrons pattern
132 */
133typedef struct _xmlSchematronPattern xmlSchematronPattern;
134typedef xmlSchematronPattern *xmlSchematronPatternPtr;
135struct _xmlSchematronPattern {
136 xmlSchematronPatternPtr next;/* the next pattern in the list */
137 xmlSchematronRulePtr rules; /* the list of rules */
138 xmlChar *name; /* the name of the pattern */
139};
140
141/**
142 * _xmlSchematron:
143 *
144 * A Schematrons definition
145 */
146struct _xmlSchematron {
147 const xmlChar *name; /* schema name */
148 int preserve; /* was the document passed by the user */
149 xmlDocPtr doc; /* pointer to the parsed document */
150 int flags; /* specific to this schematron */
151
152 void *_private; /* unused by the library */
153 xmlDictPtr dict; /* the dictionary used internally */
154
155 const xmlChar *title; /* the title if any */
156
157 int nbNs; /* the number of namespaces */
158
159 int nbPattern; /* the number of patterns */
160 xmlSchematronPatternPtr patterns;/* the patterns found */
161 xmlSchematronRulePtr rules; /* the rules gathered */
162 int nbNamespaces; /* number of namespaces in the array */
163 int maxNamespaces; /* size of the array */
164 const xmlChar **namespaces; /* the array of namespaces */
165};
166
167/**
168 * xmlSchematronValidCtxt:
169 *
170 * A Schematrons validation context
171 */
172struct _xmlSchematronValidCtxt {
173 int type;
174 int flags; /* an or of xmlSchematronValidOptions */
175
176 xmlDictPtr dict;
177 int nberrors;
178 int err;
179
180 xmlSchematronPtr schema;
181 xmlXPathContextPtr xctxt;
182
183 FILE *outputFile; /* if using XML_SCHEMATRON_OUT_FILE */
184 xmlBufferPtr outputBuffer; /* if using XML_SCHEMATRON_OUT_BUFFER */
185#ifdef LIBXML_OUTPUT_ENABLED
186 xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
187 xmlOutputCloseCallback ioclose;
188#endif
189 void *ioctx;
190
191 /* error reporting data */
192 void *userData; /* user specific data block */
193 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
194 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
195 xmlStructuredErrorFunc serror; /* the structured function */
196};
197
198struct _xmlSchematronParserCtxt {
199 int type;
200 const xmlChar *URL;
201 xmlDocPtr doc;
202 int preserve; /* Whether the doc should be freed */
203 const char *buffer;
204 int size;
205
206 xmlDictPtr dict; /* dictionary for interned string names */
207
208 int nberrors;
209 int err;
210 xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
211 xmlSchematronPtr schema;
212
213 int nbNamespaces; /* number of namespaces in the array */
214 int maxNamespaces; /* size of the array */
215 const xmlChar **namespaces; /* the array of namespaces */
216
217 int nbIncludes; /* number of includes in the array */
218 int maxIncludes; /* size of the array */
219 xmlNodePtr *includes; /* the array of includes */
220
221 /* error reporting data */
222 void *userData; /* user specific data block */
223 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
224 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
225 xmlStructuredErrorFunc serror; /* the structured function */
226};
227
228#define XML_STRON_CTXT_PARSER 1
229#define XML_STRON_CTXT_VALIDATOR 2
230
231/************************************************************************
232 * *
233 * Error reporting *
234 * *
235 ************************************************************************/
236
237/**
238 * xmlSchematronPErrMemory:
239 * @node: a context node
240 * @extra: extra information
241 *
242 * Handle an out of memory condition
243 */
244static void
245xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
246 const char *extra, xmlNodePtr node)
247{
248 if (ctxt != NULL)
249 ctxt->nberrors++;
250 __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
251 extra);
252}
253
254/**
255 * xmlSchematronPErr:
256 * @ctxt: the parsing context
257 * @node: the context node
258 * @error: the error code
259 * @msg: the error message
260 * @str1: extra data
261 * @str2: extra data
262 *
263 * Handle a parser error
264 */
265static void LIBXML_ATTR_FORMAT(4,0)
266xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
267 const char *msg, const xmlChar * str1, const xmlChar * str2)
268{
269 xmlGenericErrorFunc channel = NULL;
270 xmlStructuredErrorFunc schannel = NULL;
271 void *data = NULL;
272
273 if (ctxt != NULL) {
274 ctxt->nberrors++;
275 channel = ctxt->error;
276 data = ctxt->userData;
277 schannel = ctxt->serror;
278 }
279 __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
280 error, XML_ERR_ERROR, NULL, 0,
281 (const char *) str1, (const char *) str2, NULL, 0, 0,
282 msg, str1, str2);
283}
284
285/**
286 * xmlSchematronVTypeErrMemory:
287 * @node: a context node
288 * @extra: extra information
289 *
290 * Handle an out of memory condition
291 */
292static void
293xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
294 const char *extra, xmlNodePtr node)
295{
296 if (ctxt != NULL) {
297 ctxt->nberrors++;
298 ctxt->err = XML_SCHEMAV_INTERNAL;
299 }
300 __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
301 extra);
302}
303
304/************************************************************************
305 * *
306 * Parsing and compilation of the Schematrontrons *
307 * *
308 ************************************************************************/
309
310/**
311 * xmlSchematronAddTest:
312 * @ctxt: the schema parsing context
313 * @type: the type of test
314 * @rule: the parent rule
315 * @node: the node hosting the test
316 * @test: the associated test
317 * @report: the associated report string
318 *
319 * Add a test to a schematron
320 *
321 * Returns the new pointer or NULL in case of error
322 */
323static xmlSchematronTestPtr
324xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
325 xmlSchematronTestType type,
326 xmlSchematronRulePtr rule,
327 xmlNodePtr node, xmlChar *test, xmlChar *report)
328{
329 xmlSchematronTestPtr ret;
330 xmlXPathCompExprPtr comp;
331
332 if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
333 (test == NULL))
334 return(NULL);
335
336 /*
337 * try first to compile the test expression
338 */
339 comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
340 if (comp == NULL) {
341 xmlSchematronPErr(ctxt, node,
342 XML_SCHEMAP_NOROOT,
343 "Failed to compile test expression %s",
344 test, NULL);
345 return(NULL);
346 }
347
348 ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
349 if (ret == NULL) {
350 xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
351 return (NULL);
352 }
353 memset(ret, 0, sizeof(xmlSchematronTest));
354 ret->type = type;
355 ret->node = node;
356 ret->test = test;
357 ret->comp = comp;
358 ret->report = report;
359 ret->next = NULL;
360 if (rule->tests == NULL) {
361 rule->tests = ret;
362 } else {
363 xmlSchematronTestPtr prev = rule->tests;
364
365 while (prev->next != NULL)
366 prev = prev->next;
367 prev->next = ret;
368 }
369 return (ret);
370}
371
372/**
373 * xmlSchematronFreeTests:
374 * @tests: a list of tests
375 *
376 * Free a list of tests.
377 */
378static void
379xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
380 xmlSchematronTestPtr next;
381
382 while (tests != NULL) {
383 next = tests->next;
384 if (tests->test != NULL)
385 xmlFree(tests->test);
386 if (tests->comp != NULL)
387 xmlXPathFreeCompExpr(tests->comp);
388 if (tests->report != NULL)
389 xmlFree(tests->report);
390 xmlFree(tests);
391 tests = next;
392 }
393}
394
395/**
396 * xmlSchematronFreeLets:
397 * @lets: a list of let variables
398 *
399 * Free a list of let variables.
400 */
401static void
402xmlSchematronFreeLets(xmlSchematronLetPtr lets) {
403 xmlSchematronLetPtr next;
404
405 while (lets != NULL) {
406 next = lets->next;
407 if (lets->name != NULL)
408 xmlFree(lets->name);
409 if (lets->comp != NULL)
410 xmlXPathFreeCompExpr(lets->comp);
411 xmlFree(lets);
412 lets = next;
413 }
414}
415
416/**
417 * xmlSchematronAddRule:
418 * @ctxt: the schema parsing context
419 * @schema: a schema structure
420 * @node: the node hosting the rule
421 * @context: the associated context string
422 * @report: the associated report string
423 *
424 * Add a rule to a schematron
425 *
426 * Returns the new pointer or NULL in case of error
427 */
428static xmlSchematronRulePtr
429xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
430 xmlSchematronPatternPtr pat, xmlNodePtr node,
431 xmlChar *context, xmlChar *report)
432{
433 xmlSchematronRulePtr ret;
434 xmlPatternPtr pattern;
435
436 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
437 (context == NULL))
438 return(NULL);
439
440 /*
441 * Try first to compile the pattern
442 */
443 pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
444 ctxt->namespaces);
445 if (pattern == NULL) {
446 xmlSchematronPErr(ctxt, node,
447 XML_SCHEMAP_NOROOT,
448 "Failed to compile context expression %s",
449 context, NULL);
450 }
451
452 ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
453 if (ret == NULL) {
454 xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
455 return (NULL);
456 }
457 memset(ret, 0, sizeof(xmlSchematronRule));
458 ret->node = node;
459 ret->context = context;
460 ret->pattern = pattern;
461 ret->report = report;
462 ret->next = NULL;
463 ret->lets = NULL;
464 if (schema->rules == NULL) {
465 schema->rules = ret;
466 } else {
467 xmlSchematronRulePtr prev = schema->rules;
468
469 while (prev->next != NULL)
470 prev = prev->next;
471 prev->next = ret;
472 }
473 ret->patnext = NULL;
474 if (pat->rules == NULL) {
475 pat->rules = ret;
476 } else {
477 xmlSchematronRulePtr prev = pat->rules;
478
479 while (prev->patnext != NULL)
480 prev = prev->patnext;
481 prev->patnext = ret;
482 }
483 return (ret);
484}
485
486/**
487 * xmlSchematronFreeRules:
488 * @rules: a list of rules
489 *
490 * Free a list of rules.
491 */
492static void
493xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
494 xmlSchematronRulePtr next;
495
496 while (rules != NULL) {
497 next = rules->next;
498 if (rules->tests)
499 xmlSchematronFreeTests(rules->tests);
500 if (rules->context != NULL)
501 xmlFree(rules->context);
502 if (rules->pattern)
503 xmlFreePattern(rules->pattern);
504 if (rules->report != NULL)
505 xmlFree(rules->report);
506 if (rules->lets != NULL)
507 xmlSchematronFreeLets(rules->lets);
508 xmlFree(rules);
509 rules = next;
510 }
511}
512
513/**
514 * xmlSchematronAddPattern:
515 * @ctxt: the schema parsing context
516 * @schema: a schema structure
517 * @node: the node hosting the pattern
518 * @id: the id or name of the pattern
519 *
520 * Add a pattern to a schematron
521 *
522 * Returns the new pointer or NULL in case of error
523 */
524static xmlSchematronPatternPtr
525xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
526 xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
527{
528 xmlSchematronPatternPtr ret;
529
530 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
531 return(NULL);
532
533 ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
534 if (ret == NULL) {
535 xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
536 return (NULL);
537 }
538 memset(ret, 0, sizeof(xmlSchematronPattern));
539 ret->name = name;
540 ret->next = NULL;
541 if (schema->patterns == NULL) {
542 schema->patterns = ret;
543 } else {
544 xmlSchematronPatternPtr prev = schema->patterns;
545
546 while (prev->next != NULL)
547 prev = prev->next;
548 prev->next = ret;
549 }
550 return (ret);
551}
552
553/**
554 * xmlSchematronFreePatterns:
555 * @patterns: a list of patterns
556 *
557 * Free a list of patterns.
558 */
559static void
560xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
561 xmlSchematronPatternPtr next;
562
563 while (patterns != NULL) {
564 next = patterns->next;
565 if (patterns->name != NULL)
566 xmlFree(patterns->name);
567 xmlFree(patterns);
568 patterns = next;
569 }
570}
571
572/**
573 * xmlSchematronNewSchematron:
574 * @ctxt: a schema validation context
575 *
576 * Allocate a new Schematron structure.
577 *
578 * Returns the newly allocated structure or NULL in case or error
579 */
580static xmlSchematronPtr
581xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
582{
583 xmlSchematronPtr ret;
584
585 ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
586 if (ret == NULL) {
587 xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
588 return (NULL);
589 }
590 memset(ret, 0, sizeof(xmlSchematron));
591 ret->dict = ctxt->dict;
592 xmlDictReference(ret->dict);
593
594 return (ret);
595}
596
597/**
598 * xmlSchematronFree:
599 * @schema: a schema structure
600 *
601 * Deallocate a Schematron structure.
602 */
603void
604xmlSchematronFree(xmlSchematronPtr schema)
605{
606 if (schema == NULL)
607 return;
608
609 if ((schema->doc != NULL) && (!(schema->preserve)))
610 xmlFreeDoc(schema->doc);
611
612 if (schema->namespaces != NULL)
613 xmlFree((char **) schema->namespaces);
614
615 xmlSchematronFreeRules(schema->rules);
616 xmlSchematronFreePatterns(schema->patterns);
617 xmlDictFree(schema->dict);
618 xmlFree(schema);
619}
620
621/**
622 * xmlSchematronNewParserCtxt:
623 * @URL: the location of the schema
624 *
625 * Create an XML Schematrons parse context for that file/resource expected
626 * to contain an XML Schematrons file.
627 *
628 * Returns the parser context or NULL in case of error
629 */
630xmlSchematronParserCtxtPtr
631xmlSchematronNewParserCtxt(const char *URL)
632{
633 xmlSchematronParserCtxtPtr ret;
634
635 if (URL == NULL)
636 return (NULL);
637
638 ret =
639 (xmlSchematronParserCtxtPtr)
640 xmlMalloc(sizeof(xmlSchematronParserCtxt));
641 if (ret == NULL) {
642 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
643 NULL);
644 return (NULL);
645 }
646 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
647 ret->type = XML_STRON_CTXT_PARSER;
648 ret->dict = xmlDictCreate();
649 ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
650 ret->includes = NULL;
651 ret->xctxt = xmlXPathNewContext(NULL);
652 if (ret->xctxt == NULL) {
653 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
654 NULL);
655 xmlSchematronFreeParserCtxt(ret);
656 return (NULL);
657 }
658 ret->xctxt->flags = XML_XPATH_CHECKNS;
659 return (ret);
660}
661
662/**
663 * xmlSchematronNewMemParserCtxt:
664 * @buffer: a pointer to a char array containing the schemas
665 * @size: the size of the array
666 *
667 * Create an XML Schematrons parse context for that memory buffer expected
668 * to contain an XML Schematrons file.
669 *
670 * Returns the parser context or NULL in case of error
671 */
672xmlSchematronParserCtxtPtr
673xmlSchematronNewMemParserCtxt(const char *buffer, int size)
674{
675 xmlSchematronParserCtxtPtr ret;
676
677 if ((buffer == NULL) || (size <= 0))
678 return (NULL);
679
680 ret =
681 (xmlSchematronParserCtxtPtr)
682 xmlMalloc(sizeof(xmlSchematronParserCtxt));
683 if (ret == NULL) {
684 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
685 NULL);
686 return (NULL);
687 }
688 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
689 ret->buffer = buffer;
690 ret->size = size;
691 ret->dict = xmlDictCreate();
692 ret->xctxt = xmlXPathNewContext(NULL);
693 if (ret->xctxt == NULL) {
694 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
695 NULL);
696 xmlSchematronFreeParserCtxt(ret);
697 return (NULL);
698 }
699 return (ret);
700}
701
702/**
703 * xmlSchematronNewDocParserCtxt:
704 * @doc: a preparsed document tree
705 *
706 * Create an XML Schematrons parse context for that document.
707 * NB. The document may be modified during the parsing process.
708 *
709 * Returns the parser context or NULL in case of error
710 */
711xmlSchematronParserCtxtPtr
712xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
713{
714 xmlSchematronParserCtxtPtr ret;
715
716 if (doc == NULL)
717 return (NULL);
718
719 ret =
720 (xmlSchematronParserCtxtPtr)
721 xmlMalloc(sizeof(xmlSchematronParserCtxt));
722 if (ret == NULL) {
723 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
724 NULL);
725 return (NULL);
726 }
727 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
728 ret->doc = doc;
729 ret->dict = xmlDictCreate();
730 /* The application has responsibility for the document */
731 ret->preserve = 1;
732 ret->xctxt = xmlXPathNewContext(doc);
733 if (ret->xctxt == NULL) {
734 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
735 NULL);
736 xmlSchematronFreeParserCtxt(ret);
737 return (NULL);
738 }
739
740 return (ret);
741}
742
743/**
744 * xmlSchematronFreeParserCtxt:
745 * @ctxt: the schema parser context
746 *
747 * Free the resources associated to the schema parser context
748 */
749void
750xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
751{
752 if (ctxt == NULL)
753 return;
754 if (ctxt->doc != NULL && !ctxt->preserve)
755 xmlFreeDoc(ctxt->doc);
756 if (ctxt->xctxt != NULL) {
757 xmlXPathFreeContext(ctxt->xctxt);
758 }
759 if (ctxt->namespaces != NULL)
760 xmlFree((char **) ctxt->namespaces);
761 xmlDictFree(ctxt->dict);
762 xmlFree(ctxt);
763}
764
765#if 0
766/**
767 * xmlSchematronPushInclude:
768 * @ctxt: the schema parser context
769 * @doc: the included document
770 * @cur: the current include node
771 *
772 * Add an included document
773 */
774static void
775xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
776 xmlDocPtr doc, xmlNodePtr cur)
777{
778 if (ctxt->includes == NULL) {
779 ctxt->maxIncludes = 10;
780 ctxt->includes = (xmlNodePtr *)
781 xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
782 if (ctxt->includes == NULL) {
783 xmlSchematronPErrMemory(NULL, "allocating parser includes",
784 NULL);
785 return;
786 }
787 ctxt->nbIncludes = 0;
788 } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
789 xmlNodePtr *tmp;
790
791 tmp = (xmlNodePtr *)
792 xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
793 sizeof(xmlNodePtr));
794 if (tmp == NULL) {
795 xmlSchematronPErrMemory(NULL, "allocating parser includes",
796 NULL);
797 return;
798 }
799 ctxt->includes = tmp;
800 ctxt->maxIncludes *= 2;
801 }
802 ctxt->includes[2 * ctxt->nbIncludes] = cur;
803 ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
804 ctxt->nbIncludes++;
805}
806
807/**
808 * xmlSchematronPopInclude:
809 * @ctxt: the schema parser context
810 *
811 * Pop an include level. The included document is being freed
812 *
813 * Returns the node immediately following the include or NULL if the
814 * include list was empty.
815 */
816static xmlNodePtr
817xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
818{
819 xmlDocPtr doc;
820 xmlNodePtr ret;
821
822 if (ctxt->nbIncludes <= 0)
823 return(NULL);
824 ctxt->nbIncludes--;
825 doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
826 ret = ctxt->includes[2 * ctxt->nbIncludes];
827 xmlFreeDoc(doc);
828 if (ret != NULL)
829 ret = ret->next;
830 if (ret == NULL)
831 return(xmlSchematronPopInclude(ctxt));
832 return(ret);
833}
834#endif
835
836/**
837 * xmlSchematronAddNamespace:
838 * @ctxt: the schema parser context
839 * @prefix: the namespace prefix
840 * @ns: the namespace name
841 *
842 * Add a namespace definition in the context
843 */
844static void
845xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
846 const xmlChar *prefix, const xmlChar *ns)
847{
848 if (ctxt->namespaces == NULL) {
849 ctxt->maxNamespaces = 10;
850 ctxt->namespaces = (const xmlChar **)
851 xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
852 if (ctxt->namespaces == NULL) {
853 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
854 NULL);
855 return;
856 }
857 ctxt->nbNamespaces = 0;
858 } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
859 const xmlChar **tmp;
860
861 tmp = (const xmlChar **)
862 xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
863 sizeof(const xmlChar *));
864 if (tmp == NULL) {
865 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
866 NULL);
867 return;
868 }
869 ctxt->namespaces = tmp;
870 ctxt->maxNamespaces *= 2;
871 }
872 ctxt->namespaces[2 * ctxt->nbNamespaces] =
873 xmlDictLookup(ctxt->dict, ns, -1);
874 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
875 xmlDictLookup(ctxt->dict, prefix, -1);
876 ctxt->nbNamespaces++;
877 ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
878 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
879
880}
881
882/**
883 * xmlSchematronParseTestReportMsg:
884 * @ctxt: the schema parser context
885 * @con: the assert or report node
886 *
887 * Format the message content of the assert or report test
888 */
889static void
890xmlSchematronParseTestReportMsg(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr con)
891{
892 xmlNodePtr child;
893 xmlXPathCompExprPtr comp;
894
895 child = con->children;
896 while (child != NULL) {
897 if ((child->type == XML_TEXT_NODE) ||
898 (child->type == XML_CDATA_SECTION_NODE))
899 /* Do Nothing */
900 {}
901 else if (IS_SCHEMATRON(child, "name")) {
902 /* Do Nothing */
903 } else if (IS_SCHEMATRON(child, "value-of")) {
904 xmlChar *select;
905
906 select = xmlGetNoNsProp(child, BAD_CAST "select");
907
908 if (select == NULL) {
909 xmlSchematronPErr(ctxt, child,
910 XML_SCHEMAV_ATTRINVALID,
911 "value-of has no select attribute",
912 NULL, NULL);
913 } else {
914 /*
915 * try first to compile the test expression
916 */
917 comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
918 if (comp == NULL) {
919 xmlSchematronPErr(ctxt, child,
920 XML_SCHEMAV_ATTRINVALID,
921 "Failed to compile select expression %s",
922 select, NULL);
923 }
924 xmlXPathFreeCompExpr(comp);
925 }
926 xmlFree(select);
927 }
928 child = child->next;
929 continue;
930 }
931}
932
933/**
934 * xmlSchematronParseRule:
935 * @ctxt: a schema validation context
936 * @rule: the rule node
937 *
938 * parse a rule element
939 */
940static void
941xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
942 xmlSchematronPatternPtr pattern,
943 xmlNodePtr rule)
944{
945 xmlNodePtr cur;
946 int nbChecks = 0;
947 xmlChar *test;
948 xmlChar *context;
949 xmlChar *report;
950 xmlChar *name;
951 xmlChar *value;
952 xmlSchematronRulePtr ruleptr;
953 xmlSchematronTestPtr testptr;
954
955 if ((ctxt == NULL) || (rule == NULL)) return;
956
957 context = xmlGetNoNsProp(rule, BAD_CAST "context");
958 if (context == NULL) {
959 xmlSchematronPErr(ctxt, rule,
960 XML_SCHEMAP_NOROOT,
961 "rule has no context attribute",
962 NULL, NULL);
963 return;
964 } else if (context[0] == 0) {
965 xmlSchematronPErr(ctxt, rule,
966 XML_SCHEMAP_NOROOT,
967 "rule has an empty context attribute",
968 NULL, NULL);
969 xmlFree(context);
970 return;
971 } else {
972 ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
973 rule, context, NULL);
974 if (ruleptr == NULL) {
975 xmlFree(context);
976 return;
977 }
978 }
979
980 cur = rule->children;
981 NEXT_SCHEMATRON(cur);
982 while (cur != NULL) {
983 if (IS_SCHEMATRON(cur, "let")) {
984 xmlXPathCompExprPtr var_comp;
985 xmlSchematronLetPtr let;
986
987 name = xmlGetNoNsProp(cur, BAD_CAST "name");
988 if (name == NULL) {
989 xmlSchematronPErr(ctxt, cur,
990 XML_SCHEMAP_NOROOT,
991 "let has no name attribute",
992 NULL, NULL);
993 return;
994 } else if (name[0] == 0) {
995 xmlSchematronPErr(ctxt, cur,
996 XML_SCHEMAP_NOROOT,
997 "let has an empty name attribute",
998 NULL, NULL);
999 xmlFree(name);
1000 return;
1001 }
1002 value = xmlGetNoNsProp(cur, BAD_CAST "value");
1003 if (value == NULL) {
1004 xmlSchematronPErr(ctxt, cur,
1005 XML_SCHEMAP_NOROOT,
1006 "let has no value attribute",
1007 NULL, NULL);
1008 return;
1009 } else if (value[0] == 0) {
1010 xmlSchematronPErr(ctxt, cur,
1011 XML_SCHEMAP_NOROOT,
1012 "let has an empty value attribute",
1013 NULL, NULL);
1014 xmlFree(value);
1015 return;
1016 }
1017
1018 var_comp = xmlXPathCtxtCompile(ctxt->xctxt, value);
1019 if (var_comp == NULL) {
1020 xmlSchematronPErr(ctxt, cur,
1021 XML_SCHEMAP_NOROOT,
1022 "Failed to compile let expression %s",
1023 value, NULL);
1024 return;
1025 }
1026
1027 let = (xmlSchematronLetPtr) malloc(sizeof(xmlSchematronLet));
1028 let->name = name;
1029 let->comp = var_comp;
1030 let->next = NULL;
1031
1032 /* add new let variable to the beginning of the list */
1033 if (ruleptr->lets != NULL) {
1034 let->next = ruleptr->lets;
1035 }
1036 ruleptr->lets = let;
1037
1038 xmlFree(value);
1039 } else if (IS_SCHEMATRON(cur, "assert")) {
1040 nbChecks++;
1041 test = xmlGetNoNsProp(cur, BAD_CAST "test");
1042 if (test == NULL) {
1043 xmlSchematronPErr(ctxt, cur,
1044 XML_SCHEMAP_NOROOT,
1045 "assert has no test attribute",
1046 NULL, NULL);
1047 } else if (test[0] == 0) {
1048 xmlSchematronPErr(ctxt, cur,
1049 XML_SCHEMAP_NOROOT,
1050 "assert has an empty test attribute",
1051 NULL, NULL);
1052 xmlFree(test);
1053 } else {
1054 xmlSchematronParseTestReportMsg(ctxt, cur);
1055 report = xmlNodeGetContent(cur);
1056
1057 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
1058 ruleptr, cur, test, report);
1059 if (testptr == NULL)
1060 xmlFree(test);
1061 }
1062 } else if (IS_SCHEMATRON(cur, "report")) {
1063 nbChecks++;
1064 test = xmlGetNoNsProp(cur, BAD_CAST "test");
1065 if (test == NULL) {
1066 xmlSchematronPErr(ctxt, cur,
1067 XML_SCHEMAP_NOROOT,
1068 "assert has no test attribute",
1069 NULL, NULL);
1070 } else if (test[0] == 0) {
1071 xmlSchematronPErr(ctxt, cur,
1072 XML_SCHEMAP_NOROOT,
1073 "assert has an empty test attribute",
1074 NULL, NULL);
1075 xmlFree(test);
1076 } else {
1077 xmlSchematronParseTestReportMsg(ctxt, cur);
1078 report = xmlNodeGetContent(cur);
1079
1080 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
1081 ruleptr, cur, test, report);
1082 if (testptr == NULL)
1083 xmlFree(test);
1084 }
1085 } else {
1086 xmlSchematronPErr(ctxt, cur,
1087 XML_SCHEMAP_NOROOT,
1088 "Expecting an assert or a report element instead of %s",
1089 cur->name, NULL);
1090 }
1091 cur = cur->next;
1092 NEXT_SCHEMATRON(cur);
1093 }
1094 if (nbChecks == 0) {
1095 xmlSchematronPErr(ctxt, rule,
1096 XML_SCHEMAP_NOROOT,
1097 "rule has no assert nor report element", NULL, NULL);
1098 }
1099}
1100
1101/**
1102 * xmlSchematronParsePattern:
1103 * @ctxt: a schema validation context
1104 * @pat: the pattern node
1105 *
1106 * parse a pattern element
1107 */
1108static void
1109xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
1110{
1111 xmlNodePtr cur;
1112 xmlSchematronPatternPtr pattern;
1113 int nbRules = 0;
1114 xmlChar *id;
1115
1116 if ((ctxt == NULL) || (pat == NULL)) return;
1117
1118 id = xmlGetNoNsProp(pat, BAD_CAST "id");
1119 if (id == NULL) {
1120 id = xmlGetNoNsProp(pat, BAD_CAST "name");
1121 }
1122 pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
1123 if (pattern == NULL) {
1124 if (id != NULL)
1125 xmlFree(id);
1126 return;
1127 }
1128 cur = pat->children;
1129 NEXT_SCHEMATRON(cur);
1130 while (cur != NULL) {
1131 if (IS_SCHEMATRON(cur, "rule")) {
1132 xmlSchematronParseRule(ctxt, pattern, cur);
1133 nbRules++;
1134 } else {
1135 xmlSchematronPErr(ctxt, cur,
1136 XML_SCHEMAP_NOROOT,
1137 "Expecting a rule element instead of %s", cur->name, NULL);
1138 }
1139 cur = cur->next;
1140 NEXT_SCHEMATRON(cur);
1141 }
1142 if (nbRules == 0) {
1143 xmlSchematronPErr(ctxt, pat,
1144 XML_SCHEMAP_NOROOT,
1145 "Pattern has no rule element", NULL, NULL);
1146 }
1147}
1148
1149#if 0
1150/**
1151 * xmlSchematronLoadInclude:
1152 * @ctxt: a schema validation context
1153 * @cur: the include element
1154 *
1155 * Load the include document, Push the current pointer
1156 *
1157 * Returns the updated node pointer
1158 */
1159static xmlNodePtr
1160xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1161{
1162 xmlNodePtr ret = NULL;
1163 xmlDocPtr doc = NULL;
1164 xmlChar *href = NULL;
1165 xmlChar *base = NULL;
1166 xmlChar *URI = NULL;
1167
1168 if ((ctxt == NULL) || (cur == NULL))
1169 return(NULL);
1170
1171 href = xmlGetNoNsProp(cur, BAD_CAST "href");
1172 if (href == NULL) {
1173 xmlSchematronPErr(ctxt, cur,
1174 XML_SCHEMAP_NOROOT,
1175 "Include has no href attribute", NULL, NULL);
1176 return(cur->next);
1177 }
1178
1179 /* do the URI base composition, load and find the root */
1180 base = xmlNodeGetBase(cur->doc, cur);
1181 URI = xmlBuildURI(href, base);
1182 doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
1183 if (doc == NULL) {
1184 xmlSchematronPErr(ctxt, cur,
1185 XML_SCHEMAP_FAILED_LOAD,
1186 "could not load include '%s'.\n",
1187 URI, NULL);
1188 goto done;
1189 }
1190 ret = xmlDocGetRootElement(doc);
1191 if (ret == NULL) {
1192 xmlSchematronPErr(ctxt, cur,
1193 XML_SCHEMAP_FAILED_LOAD,
1194 "could not find root from include '%s'.\n",
1195 URI, NULL);
1196 goto done;
1197 }
1198
1199 /* Success, push the include for rollback on exit */
1200 xmlSchematronPushInclude(ctxt, doc, cur);
1201
1202done:
1203 if (ret == NULL) {
1204 if (doc != NULL)
1205 xmlFreeDoc(doc);
1206 }
1207 xmlFree(href);
1208 if (base != NULL)
1209 xmlFree(base);
1210 if (URI != NULL)
1211 xmlFree(URI);
1212 return(ret);
1213}
1214#endif
1215
1216/**
1217 * xmlSchematronParse:
1218 * @ctxt: a schema validation context
1219 *
1220 * parse a schema definition resource and build an internal
1221 * XML Schema structure which can be used to validate instances.
1222 *
1223 * Returns the internal XML Schematron structure built from the resource or
1224 * NULL in case of error
1225 */
1226xmlSchematronPtr
1227xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1228{
1229 xmlSchematronPtr ret = NULL;
1230 xmlDocPtr doc;
1231 xmlNodePtr root, cur;
1232 int preserve = 0;
1233
1234 if (ctxt == NULL)
1235 return (NULL);
1236
1237 ctxt->nberrors = 0;
1238
1239 /*
1240 * First step is to parse the input document into an DOM/Infoset
1241 */
1242 if (ctxt->URL != NULL) {
1243 doc = xmlReadFile((const char *) ctxt->URL, NULL,
1244 SCHEMATRON_PARSE_OPTIONS);
1245 if (doc == NULL) {
1246 xmlSchematronPErr(ctxt, NULL,
1247 XML_SCHEMAP_FAILED_LOAD,
1248 "xmlSchematronParse: could not load '%s'.\n",
1249 ctxt->URL, NULL);
1250 return (NULL);
1251 }
1252 ctxt->preserve = 0;
1253 } else if (ctxt->buffer != NULL) {
1254 doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1255 SCHEMATRON_PARSE_OPTIONS);
1256 if (doc == NULL) {
1257 xmlSchematronPErr(ctxt, NULL,
1258 XML_SCHEMAP_FAILED_PARSE,
1259 "xmlSchematronParse: could not parse.\n",
1260 NULL, NULL);
1261 return (NULL);
1262 }
1263 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1264 ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
1265 ctxt->preserve = 0;
1266 } else if (ctxt->doc != NULL) {
1267 doc = ctxt->doc;
1268 preserve = 1;
1269 ctxt->preserve = 1;
1270 } else {
1271 xmlSchematronPErr(ctxt, NULL,
1272 XML_SCHEMAP_NOTHING_TO_PARSE,
1273 "xmlSchematronParse: could not parse.\n",
1274 NULL, NULL);
1275 return (NULL);
1276 }
1277
1278 /*
1279 * Then extract the root and Schematron parse it
1280 */
1281 root = xmlDocGetRootElement(doc);
1282 if (root == NULL) {
1283 xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1284 XML_SCHEMAP_NOROOT,
1285 "The schema has no document element.\n", NULL, NULL);
1286 if (!preserve) {
1287 xmlFreeDoc(doc);
1288 }
1289 return (NULL);
1290 }
1291
1292 if (!IS_SCHEMATRON(root, "schema")) {
1293 xmlSchematronPErr(ctxt, root,
1294 XML_SCHEMAP_NOROOT,
1295 "The XML document '%s' is not a XML schematron document",
1296 ctxt->URL, NULL);
1297 goto exit;
1298 }
1299 ret = xmlSchematronNewSchematron(ctxt);
1300 if (ret == NULL)
1301 goto exit;
1302 ctxt->schema = ret;
1303
1304 /*
1305 * scan the schema elements
1306 */
1307 cur = root->children;
1308 NEXT_SCHEMATRON(cur);
1309 if (IS_SCHEMATRON(cur, "title")) {
1310 xmlChar *title = xmlNodeGetContent(cur);
1311 if (title != NULL) {
1312 ret->title = xmlDictLookup(ret->dict, title, -1);
1313 xmlFree(title);
1314 }
1315 cur = cur->next;
1316 NEXT_SCHEMATRON(cur);
1317 }
1318 while (IS_SCHEMATRON(cur, "ns")) {
1319 xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1320 xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1321 if ((uri == NULL) || (uri[0] == 0)) {
1322 xmlSchematronPErr(ctxt, cur,
1323 XML_SCHEMAP_NOROOT,
1324 "ns element has no uri", NULL, NULL);
1325 }
1326 if ((prefix == NULL) || (prefix[0] == 0)) {
1327 xmlSchematronPErr(ctxt, cur,
1328 XML_SCHEMAP_NOROOT,
1329 "ns element has no prefix", NULL, NULL);
1330 }
1331 if ((prefix) && (uri)) {
1332 xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1333 xmlSchematronAddNamespace(ctxt, prefix, uri);
1334 ret->nbNs++;
1335 }
1336 if (uri)
1337 xmlFree(uri);
1338 if (prefix)
1339 xmlFree(prefix);
1340 cur = cur->next;
1341 NEXT_SCHEMATRON(cur);
1342 }
1343 while (cur != NULL) {
1344 if (IS_SCHEMATRON(cur, "pattern")) {
1345 xmlSchematronParsePattern(ctxt, cur);
1346 ret->nbPattern++;
1347 } else {
1348 xmlSchematronPErr(ctxt, cur,
1349 XML_SCHEMAP_NOROOT,
1350 "Expecting a pattern element instead of %s", cur->name, NULL);
1351 }
1352 cur = cur->next;
1353 NEXT_SCHEMATRON(cur);
1354 }
1355 if (ret->nbPattern == 0) {
1356 xmlSchematronPErr(ctxt, root,
1357 XML_SCHEMAP_NOROOT,
1358 "The schematron document '%s' has no pattern",
1359 ctxt->URL, NULL);
1360 goto exit;
1361 }
1362 /* the original document must be kept for reporting */
1363 ret->doc = doc;
1364 if (preserve) {
1365 ret->preserve = 1;
1366 }
1367 preserve = 1;
1368
1369exit:
1370 if (!preserve) {
1371 xmlFreeDoc(doc);
1372 }
1373 if (ret != NULL) {
1374 if (ctxt->nberrors != 0) {
1375 xmlSchematronFree(ret);
1376 ret = NULL;
1377 } else {
1378 ret->namespaces = ctxt->namespaces;
1379 ret->nbNamespaces = ctxt->nbNamespaces;
1380 ctxt->namespaces = NULL;
1381 }
1382 }
1383 return (ret);
1384}
1385
1386/************************************************************************
1387 * *
1388 * Schematrontron Reports handler *
1389 * *
1390 ************************************************************************/
1391
1392static xmlNodePtr
1393xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1394 xmlNodePtr cur, const xmlChar *xpath) {
1395 xmlNodePtr node = NULL;
1396 xmlXPathObjectPtr ret;
1397
1398 if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1399 return(NULL);
1400
1401 ctxt->xctxt->doc = cur->doc;
1402 ctxt->xctxt->node = cur;
1403 ret = xmlXPathEval(xpath, ctxt->xctxt);
1404 if (ret == NULL)
1405 return(NULL);
1406
1407 if ((ret->type == XPATH_NODESET) &&
1408 (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1409 node = ret->nodesetval->nodeTab[0];
1410
1411 xmlXPathFreeObject(ret);
1412 return(node);
1413}
1414
1415/**
1416 * xmlSchematronReportOutput:
1417 * @ctxt: the validation context
1418 * @cur: the current node tested
1419 * @msg: the message output
1420 *
1421 * Output part of the report to whatever channel the user selected
1422 */
1423static void
1424xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1425 xmlNodePtr cur ATTRIBUTE_UNUSED,
1426 const char *msg) {
1427 /* TODO */
1428 fprintf(stderr, "%s", msg);
1429}
1430
1431/**
1432 * xmlSchematronFormatReport:
1433 * @ctxt: the validation context
1434 * @test: the test node
1435 * @cur: the current node tested
1436 *
1437 * Build the string being reported to the user.
1438 *
1439 * Returns a report string or NULL in case of error. The string needs
1440 * to be deallocated by the caller
1441 */
1442static xmlChar *
1443xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1444 xmlNodePtr test, xmlNodePtr cur) {
1445 xmlChar *ret = NULL;
1446 xmlNodePtr child, node;
1447 xmlXPathCompExprPtr comp;
1448
1449 if ((test == NULL) || (cur == NULL))
1450 return(ret);
1451
1452 child = test->children;
1453 while (child != NULL) {
1454 if ((child->type == XML_TEXT_NODE) ||
1455 (child->type == XML_CDATA_SECTION_NODE))
1456 ret = xmlStrcat(ret, child->content);
1457 else if (IS_SCHEMATRON(child, "name")) {
1458 xmlChar *path;
1459
1460 path = xmlGetNoNsProp(child, BAD_CAST "path");
1461
1462 node = cur;
1463 if (path != NULL) {
1464 node = xmlSchematronGetNode(ctxt, cur, path);
1465 if (node == NULL)
1466 node = cur;
1467 xmlFree(path);
1468 }
1469
1470 if ((node->ns == NULL) || (node->ns->prefix == NULL))
1471 ret = xmlStrcat(ret, node->name);
1472 else {
1473 ret = xmlStrcat(ret, node->ns->prefix);
1474 ret = xmlStrcat(ret, BAD_CAST ":");
1475 ret = xmlStrcat(ret, node->name);
1476 }
1477 } else if (IS_SCHEMATRON(child, "value-of")) {
1478 xmlChar *select;
1479 xmlXPathObjectPtr eval;
1480
1481 select = xmlGetNoNsProp(child, BAD_CAST "select");
1482 comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
1483 eval = xmlXPathCompiledEval(comp, ctxt->xctxt);
1484
1485 switch (eval->type) {
1486 case XPATH_NODESET: {
1487 int indx;
1488 xmlChar *spacer = BAD_CAST " ";
1489
1490 if (eval->nodesetval) {
1491 for (indx = 0; indx < eval->nodesetval->nodeNr; indx++) {
1492 if (indx > 0)
1493 ret = xmlStrcat(ret, spacer);
1494 ret = xmlStrcat(ret, eval->nodesetval->nodeTab[indx]->name);
1495 }
1496 } else {
1497 xmlGenericError(xmlGenericErrorContext,
1498 "Empty node set\n");
1499 }
1500 break;
1501 }
1502 case XPATH_BOOLEAN: {
1503 const char *str = eval->boolval ? "True" : "False";
1504 ret = xmlStrcat(ret, BAD_CAST str);
1505 break;
1506 }
1507 case XPATH_NUMBER: {
1508 xmlChar *buf;
1509 int size;
1510
1511 size = snprintf(NULL, 0, "%0g", eval->floatval);
1512 buf = (xmlChar *) xmlMalloc(size + 1);
1513 if (buf != NULL) {
1514 snprintf((char *) buf, size + 1, "%0g", eval->floatval);
1515 ret = xmlStrcat(ret, buf);
1516 xmlFree(buf);
1517 }
1518 break;
1519 }
1520 case XPATH_STRING:
1521 ret = xmlStrcat(ret, eval->stringval);
1522 break;
1523 default:
1524 xmlGenericError(xmlGenericErrorContext,
1525 "Unsupported XPATH Type: %d\n", eval->type);
1526 }
1527 xmlXPathFreeObject(eval);
1528 xmlXPathFreeCompExpr(comp);
1529 xmlFree(select);
1530 } else {
1531 child = child->next;
1532 continue;
1533 }
1534
1535 /*
1536 * remove superfluous \n
1537 */
1538 if (ret != NULL) {
1539 int len = xmlStrlen(ret);
1540 xmlChar c;
1541
1542 if (len > 0) {
1543 c = ret[len - 1];
1544 if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1545 while ((c == ' ') || (c == '\n') ||
1546 (c == '\r') || (c == '\t')) {
1547 len--;
1548 if (len == 0)
1549 break;
1550 c = ret[len - 1];
1551 }
1552 ret[len] = ' ';
1553 ret[len + 1] = 0;
1554 }
1555 }
1556 }
1557
1558 child = child->next;
1559 }
1560 return(ret);
1561}
1562
1563/**
1564 * xmlSchematronReportSuccess:
1565 * @ctxt: the validation context
1566 * @test: the compiled test
1567 * @cur: the current node tested
1568 * @success: boolean value for the result
1569 *
1570 * called from the validation engine when an assert or report test have
1571 * been done.
1572 */
1573static void
1574xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
1575 xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
1576 if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1577 return;
1578 /* if quiet and not SVRL report only failures */
1579 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1580 ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
1581 (test->type == XML_SCHEMATRON_REPORT))
1582 return;
1583 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1584 TODO
1585 } else {
1586 xmlChar *path;
1587 char msg[1000];
1588 long line;
1589 const xmlChar *report = NULL;
1590
1591 if (((test->type == XML_SCHEMATRON_REPORT) && (!success)) ||
1592 ((test->type == XML_SCHEMATRON_ASSERT) && (success)))
1593 return;
1594 line = xmlGetLineNo(cur);
1595 path = xmlGetNodePath(cur);
1596 if (path == NULL)
1597 path = (xmlChar *) cur->name;
1598#if 0
1599 if ((test->report != NULL) && (test->report[0] != 0))
1600 report = test->report;
1601#endif
1602 if (test->node != NULL)
1603 report = xmlSchematronFormatReport(ctxt, test->node, cur);
1604 if (report == NULL) {
1605 if (test->type == XML_SCHEMATRON_ASSERT) {
1606 report = xmlStrdup((const xmlChar *) "node failed assert");
1607 } else {
1608 report = xmlStrdup((const xmlChar *) "node failed report");
1609 }
1610 }
1611 snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1612 line, (const char *) report);
1613
1614 if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
1615 xmlStructuredErrorFunc schannel = NULL;
1616 xmlGenericErrorFunc channel = NULL;
1617 void *data = NULL;
1618
1619 if (ctxt != NULL) {
1620 if (ctxt->serror != NULL)
1621 schannel = ctxt->serror;
1622 else
1623 channel = ctxt->error;
1624 data = ctxt->userData;
1625 }
1626
1627 __xmlRaiseError(schannel, channel, data,
1628 NULL, cur, XML_FROM_SCHEMATRONV,
1629 (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,
1630 XML_ERR_ERROR, NULL, line,
1631 (pattern == NULL)?NULL:((const char *) pattern->name),
1632 (const char *) path,
1633 (const char *) report, 0, 0,
1634 "%s", msg);
1635 } else {
1636 xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1637 }
1638
1639 xmlFree((char *) report);
1640
1641 if ((path != NULL) && (path != (xmlChar *) cur->name))
1642 xmlFree(path);
1643 }
1644}
1645
1646/**
1647 * xmlSchematronReportPattern:
1648 * @ctxt: the validation context
1649 * @pattern: the current pattern
1650 *
1651 * called from the validation engine when starting to check a pattern
1652 */
1653static void
1654xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1655 xmlSchematronPatternPtr pattern) {
1656 if ((ctxt == NULL) || (pattern == NULL))
1657 return;
1658 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
1659 return;
1660 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1661 TODO
1662 } else {
1663 char msg[1000];
1664
1665 if (pattern->name == NULL)
1666 return;
1667 snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1668 xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1669 }
1670}
1671
1672
1673/************************************************************************
1674 * *
1675 * Validation against a Schematrontron *
1676 * *
1677 ************************************************************************/
1678
1679/**
1680 * xmlSchematronSetValidStructuredErrors:
1681 * @ctxt: a Schematron validation context
1682 * @serror: the structured error function
1683 * @ctx: the functions context
1684 *
1685 * Set the structured error callback
1686 */
1687void
1688xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
1689 xmlStructuredErrorFunc serror, void *ctx)
1690{
1691 if (ctxt == NULL)
1692 return;
1693 ctxt->serror = serror;
1694 ctxt->error = NULL;
1695 ctxt->warning = NULL;
1696 ctxt->userData = ctx;
1697}
1698
1699/**
1700 * xmlSchematronNewValidCtxt:
1701 * @schema: a precompiled XML Schematrons
1702 * @options: a set of xmlSchematronValidOptions
1703 *
1704 * Create an XML Schematrons validation context based on the given schema.
1705 *
1706 * Returns the validation context or NULL in case of error
1707 */
1708xmlSchematronValidCtxtPtr
1709xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1710{
1711 int i;
1712 xmlSchematronValidCtxtPtr ret;
1713
1714 ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
1715 if (ret == NULL) {
1716 xmlSchematronVErrMemory(NULL, "allocating validation context",
1717 NULL);
1718 return (NULL);
1719 }
1720 memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1721 ret->type = XML_STRON_CTXT_VALIDATOR;
1722 ret->schema = schema;
1723 ret->xctxt = xmlXPathNewContext(NULL);
1724 ret->flags = options;
1725 if (ret->xctxt == NULL) {
1726 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
1727 NULL);
1728 xmlSchematronFreeValidCtxt(ret);
1729 return (NULL);
1730 }
1731 for (i = 0;i < schema->nbNamespaces;i++) {
1732 if ((schema->namespaces[2 * i] == NULL) ||
1733 (schema->namespaces[2 * i + 1] == NULL))
1734 break;
1735 xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1736 schema->namespaces[2 * i]);
1737 }
1738 return (ret);
1739}
1740
1741/**
1742 * xmlSchematronFreeValidCtxt:
1743 * @ctxt: the schema validation context
1744 *
1745 * Free the resources associated to the schema validation context
1746 */
1747void
1748xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1749{
1750 if (ctxt == NULL)
1751 return;
1752 if (ctxt->xctxt != NULL)
1753 xmlXPathFreeContext(ctxt->xctxt);
1754 if (ctxt->dict != NULL)
1755 xmlDictFree(ctxt->dict);
1756 xmlFree(ctxt);
1757}
1758
1759static xmlNodePtr
1760xmlSchematronNextNode(xmlNodePtr cur) {
1761 if (cur->children != NULL) {
1762 /*
1763 * Do not descend on entities declarations
1764 */
1765 if (cur->children->type != XML_ENTITY_DECL) {
1766 cur = cur->children;
1767 /*
1768 * Skip DTDs
1769 */
1770 if (cur->type != XML_DTD_NODE)
1771 return(cur);
1772 }
1773 }
1774
1775 while (cur->next != NULL) {
1776 cur = cur->next;
1777 if ((cur->type != XML_ENTITY_DECL) &&
1778 (cur->type != XML_DTD_NODE))
1779 return(cur);
1780 }
1781
1782 do {
1783 cur = cur->parent;
1784 if (cur == NULL) break;
1785 if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1786 if (cur->next != NULL) {
1787 cur = cur->next;
1788 return(cur);
1789 }
1790 } while (cur != NULL);
1791 return(cur);
1792}
1793
1794/**
1795 * xmlSchematronRunTest:
1796 * @ctxt: the schema validation context
1797 * @test: the current test
1798 * @instance: the document instance tree
1799 * @cur: the current node in the instance
1800 *
1801 * Validate a rule against a tree instance at a given position
1802 *
1803 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1804 */
1805static int
1806xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1807 xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
1808{
1809 xmlXPathObjectPtr ret;
1810 int failed;
1811
1812 failed = 0;
1813 ctxt->xctxt->doc = instance;
1814 ctxt->xctxt->node = cur;
1815 ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1816 if (ret == NULL) {
1817 failed = 1;
1818 } else {
1819 switch (ret->type) {
1820 case XPATH_XSLT_TREE:
1821 case XPATH_NODESET:
1822 if ((ret->nodesetval == NULL) ||
1823 (ret->nodesetval->nodeNr == 0))
1824 failed = 1;
1825 break;
1826 case XPATH_BOOLEAN:
1827 failed = !ret->boolval;
1828 break;
1829 case XPATH_NUMBER:
1830 if ((xmlXPathIsNaN(ret->floatval)) ||
1831 (ret->floatval == 0.0))
1832 failed = 1;
1833 break;
1834 case XPATH_STRING:
1835 if ((ret->stringval == NULL) ||
1836 (ret->stringval[0] == 0))
1837 failed = 1;
1838 break;
1839 case XPATH_UNDEFINED:
1840#ifdef LIBXML_XPTR_LOCS_ENABLED
1841 case XPATH_POINT:
1842 case XPATH_RANGE:
1843 case XPATH_LOCATIONSET:
1844#endif
1845 case XPATH_USERS:
1846 failed = 1;
1847 break;
1848 }
1849 xmlXPathFreeObject(ret);
1850 }
1851 if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
1852 ctxt->nberrors++;
1853 else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1854 ctxt->nberrors++;
1855
1856 xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
1857
1858 return(!failed);
1859}
1860
1861/**
1862 * xmlSchematronRegisterVariables:
1863 * @ctxt: the schema validation context
1864 * @let: the list of let variables
1865 * @instance: the document instance tree
1866 * @cur: the current node
1867 *
1868 * Registers a list of let variables to the current context of @cur
1869 *
1870 * Returns -1 in case of errors, otherwise 0
1871 */
1872static int
1873xmlSchematronRegisterVariables(xmlXPathContextPtr ctxt, xmlSchematronLetPtr let,
1874 xmlDocPtr instance, xmlNodePtr cur)
1875{
1876 xmlXPathObjectPtr let_eval;
1877
1878 ctxt->doc = instance;
1879 ctxt->node = cur;
1880 while (let != NULL) {
1881 let_eval = xmlXPathCompiledEval(let->comp, ctxt);
1882 if (let_eval == NULL) {
1883 xmlGenericError(xmlGenericErrorContext,
1884 "Evaluation of compiled expression failed\n");
1885 return -1;
1886 }
1887 if(xmlXPathRegisterVariableNS(ctxt, let->name, NULL, let_eval)) {
1888 xmlGenericError(xmlGenericErrorContext,
1889 "Registering a let variable failed\n");
1890 return -1;
1891 }
1892 let = let->next;
1893 }
1894 return 0;
1895}
1896
1897/**
1898 * xmlSchematronUnregisterVariables:
1899 * @ctxt: the schema validation context
1900 * @let: the list of let variables
1901 *
1902 * Unregisters a list of let variables from the context
1903 *
1904 * Returns -1 in case of errors, otherwise 0
1905 */
1906static int
1907xmlSchematronUnregisterVariables(xmlXPathContextPtr ctxt, xmlSchematronLetPtr let)
1908{
1909 while (let != NULL) {
1910 if (xmlXPathRegisterVariableNS(ctxt, let->name, NULL, NULL)) {
1911 xmlGenericError(xmlGenericErrorContext,
1912 "Unregistering a let variable failed\n");
1913 return -1;
1914 }
1915 let = let->next;
1916 }
1917 return 0;
1918}
1919
1920/**
1921 * xmlSchematronValidateDoc:
1922 * @ctxt: the schema validation context
1923 * @instance: the document instance tree
1924 *
1925 * Validate a tree instance against the schematron
1926 *
1927 * Returns 0 in case of success, -1 in case of internal error
1928 * and an error count otherwise.
1929 */
1930int
1931xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1932{
1933 xmlNodePtr cur, root;
1934 xmlSchematronPatternPtr pattern;
1935 xmlSchematronRulePtr rule;
1936 xmlSchematronTestPtr test;
1937
1938 if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1939 (ctxt->schema->rules == NULL) || (instance == NULL))
1940 return(-1);
1941 ctxt->nberrors = 0;
1942 root = xmlDocGetRootElement(instance);
1943 if (root == NULL) {
1944 TODO
1945 ctxt->nberrors++;
1946 return(1);
1947 }
1948 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1949 (ctxt->flags == 0)) {
1950 /*
1951 * we are just trying to assert the validity of the document,
1952 * speed primes over the output, run in a single pass
1953 */
1954 cur = root;
1955 while (cur != NULL) {
1956 rule = ctxt->schema->rules;
1957 while (rule != NULL) {
1958 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1959 test = rule->tests;
1960
1961 if (xmlSchematronRegisterVariables(ctxt->xctxt, rule->lets, instance, cur))
1962 return -1;
1963
1964 while (test != NULL) {
1965 xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
1966 test = test->next;
1967 }
1968
1969 if (xmlSchematronUnregisterVariables(ctxt->xctxt, rule->lets))
1970 return -1;
1971
1972 }
1973 rule = rule->next;
1974 }
1975
1976 cur = xmlSchematronNextNode(cur);
1977 }
1978 } else {
1979 /*
1980 * Process all contexts one at a time
1981 */
1982 pattern = ctxt->schema->patterns;
1983
1984 while (pattern != NULL) {
1985 xmlSchematronReportPattern(ctxt, pattern);
1986
1987 /*
1988 * TODO convert the pattern rule to a direct XPath and
1989 * compute directly instead of using the pattern matching
1990 * over the full document...
1991 * Check the exact semantic
1992 */
1993 cur = root;
1994 while (cur != NULL) {
1995 rule = pattern->rules;
1996 while (rule != NULL) {
1997 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1998 test = rule->tests;
1999 xmlSchematronRegisterVariables(ctxt->xctxt, rule->lets,
2000 instance, cur);
2001
2002 while (test != NULL) {
2003 xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
2004 test = test->next;
2005 }
2006
2007 xmlSchematronUnregisterVariables(ctxt->xctxt, rule->lets);
2008 }
2009 rule = rule->patnext;
2010 }
2011
2012 cur = xmlSchematronNextNode(cur);
2013 }
2014 pattern = pattern->next;
2015 }
2016 }
2017 return(ctxt->nberrors);
2018}
2019
2020#ifdef STANDALONE
2021int
2022main(void)
2023{
2024 int ret;
2025 xmlDocPtr instance;
2026 xmlSchematronParserCtxtPtr pctxt;
2027 xmlSchematronValidCtxtPtr vctxt;
2028 xmlSchematronPtr schema = NULL;
2029
2030 pctxt = xmlSchematronNewParserCtxt("tst.sct");
2031 if (pctxt == NULL) {
2032 fprintf(stderr, "failed to build schematron parser\n");
2033 } else {
2034 schema = xmlSchematronParse(pctxt);
2035 if (schema == NULL) {
2036 fprintf(stderr, "failed to compile schematron\n");
2037 }
2038 xmlSchematronFreeParserCtxt(pctxt);
2039 }
2040 instance = xmlReadFile("tst.sct", NULL,
2041 XML_PARSE_NOENT | XML_PARSE_NOCDATA);
2042 if (instance == NULL) {
2043 fprintf(stderr, "failed to parse instance\n");
2044 }
2045 if ((schema != NULL) && (instance != NULL)) {
2046 vctxt = xmlSchematronNewValidCtxt(schema);
2047 if (vctxt == NULL) {
2048 fprintf(stderr, "failed to build schematron validator\n");
2049 } else {
2050 ret = xmlSchematronValidateDoc(vctxt, instance);
2051 xmlSchematronFreeValidCtxt(vctxt);
2052 }
2053 }
2054 xmlSchematronFree(schema);
2055 xmlFreeDoc(instance);
2056
2057 xmlCleanupParser();
2058
2059 return (0);
2060}
2061#endif
2062
2063#endif /* LIBXML_SCHEMATRON_ENABLED */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use