VirtualBox

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

Last change on this file was 105420, checked in by vboxsync, 4 months ago

libxml2-2.12.6: Applied and adjusted our libxml2 changes to 2.12.6. bugref:10730

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette