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