mirror of
				https://gitlab.gnome.org/GNOME/libxml2.git
				synced 2025-10-30 10:45:36 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			1786 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1786 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * schematron.c : implementation of the Schematron schema validity checking
 | |
|  *
 | |
|  * See Copyright for the status of this software.
 | |
|  *
 | |
|  * Daniel Veillard <daniel@veillard.com>
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * TODO:
 | |
|  * + double check the semantic, especially
 | |
|  *        - multiple rules applying in a single pattern/node
 | |
|  *        - the semantic of libxml2 patterns vs. XSLT production referenced
 | |
|  *          by the spec.
 | |
|  * + export of results in SVRL
 | |
|  * + full parsing and coverage of the spec, conformance of the input to the
 | |
|  *   spec
 | |
|  * + divergences between the draft and the ISO proposed standard :-(
 | |
|  * + hook and test include
 | |
|  * + try and compare with the XSLT version
 | |
|  */
 | |
| 
 | |
| #define IN_LIBXML
 | |
| #include "libxml.h"
 | |
| 
 | |
| #ifdef LIBXML_SCHEMATRON_ENABLED
 | |
| 
 | |
| #include <string.h>
 | |
| #include <libxml/parser.h>
 | |
| #include <libxml/tree.h>
 | |
| #include <libxml/uri.h>
 | |
| #include <libxml/xpath.h>
 | |
| #include <libxml/xpathInternals.h>
 | |
| #include <libxml/pattern.h>
 | |
| #include <libxml/schematron.h>
 | |
| 
 | |
| #define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
 | |
| 
 | |
| #define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
 | |
| 
 | |
| #define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
 | |
| 
 | |
| 
 | |
| static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
 | |
| static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
 | |
| 
 | |
| #define IS_SCHEMATRON(node, elem)					\
 | |
|    ((node != NULL) && (node->type == XML_ELEMENT_NODE ) &&		\
 | |
|     (node->ns != NULL) &&						\
 | |
|     (xmlStrEqual(node->name, (const xmlChar *) elem)) &&		\
 | |
|     ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||			\
 | |
|      (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
 | |
| 
 | |
| #define NEXT_SCHEMATRON(node)						\
 | |
|    while (node != NULL) {						\
 | |
|        if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) &&	\
 | |
|            ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||		\
 | |
| 	    (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))		\
 | |
| 	   break;							\
 | |
|        node = node->next;						\
 | |
|    }
 | |
| 
 | |
| /**
 | |
|  * TODO:
 | |
|  *
 | |
|  * macro to flag unimplemented blocks
 | |
|  */
 | |
| #define TODO								\
 | |
|     xmlGenericError(xmlGenericErrorContext,				\
 | |
| 	    "Unimplemented block at %s:%d\n",				\
 | |
|             __FILE__, __LINE__);
 | |
| 
 | |
| typedef enum {
 | |
|     XML_SCHEMATRON_ASSERT=1,
 | |
|     XML_SCHEMATRON_REPORT=2
 | |
| } xmlSchematronTestType;
 | |
| 
 | |
| /**
 | |
|  * _xmlSchematronTest:
 | |
|  *
 | |
|  * A Schematrons test, either an assert or a report
 | |
|  */
 | |
| typedef struct _xmlSchematronTest xmlSchematronTest;
 | |
| typedef xmlSchematronTest *xmlSchematronTestPtr;
 | |
| struct _xmlSchematronTest {
 | |
|     xmlSchematronTestPtr next;	/* the next test in the list */
 | |
|     xmlSchematronTestType type;	/* the test type */
 | |
|     xmlNodePtr node;		/* the node in the tree */
 | |
|     xmlChar *test;		/* the expression to test */
 | |
|     xmlXPathCompExprPtr comp;	/* the compiled expression */
 | |
|     xmlChar *report;		/* the message to report */
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * _xmlSchematronRule:
 | |
|  *
 | |
|  * A Schematrons rule
 | |
|  */
 | |
| typedef struct _xmlSchematronRule xmlSchematronRule;
 | |
| typedef xmlSchematronRule *xmlSchematronRulePtr;
 | |
| struct _xmlSchematronRule {
 | |
|     xmlSchematronRulePtr next;	/* the next rule in the list */
 | |
|     xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
 | |
|     xmlNodePtr node;		/* the node in the tree */
 | |
|     xmlChar *context;		/* the context evaluation rule */
 | |
|     xmlSchematronTestPtr tests;	/* the list of tests */
 | |
|     xmlPatternPtr pattern;	/* the compiled pattern associated */
 | |
|     xmlChar *report;		/* the message to report */
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * _xmlSchematronPattern:
 | |
|  *
 | |
|  * A Schematrons pattern
 | |
|  */
 | |
| typedef struct _xmlSchematronPattern xmlSchematronPattern;
 | |
| typedef xmlSchematronPattern *xmlSchematronPatternPtr;
 | |
| struct _xmlSchematronPattern {
 | |
|     xmlSchematronPatternPtr next;/* the next pattern in the list */
 | |
|     xmlSchematronRulePtr rules;	/* the list of rules */
 | |
|     xmlChar *name;		/* the name of the pattern */
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * _xmlSchematron:
 | |
|  *
 | |
|  * A Schematrons definition
 | |
|  */
 | |
| struct _xmlSchematron {
 | |
|     const xmlChar *name;	/* schema name */
 | |
|     int preserve;		/* was the document passed by the user */
 | |
|     xmlDocPtr doc;		/* pointer to the parsed document */
 | |
|     int flags;			/* specific to this schematron */
 | |
| 
 | |
|     void *_private;		/* unused by the library */
 | |
|     xmlDictPtr dict;		/* the dictionnary used internally */
 | |
| 
 | |
|     const xmlChar *title;	/* the title if any */
 | |
| 
 | |
|     int nbNs;			/* the number of namespaces */
 | |
| 
 | |
|     int nbPattern;		/* the number of patterns */
 | |
|     xmlSchematronPatternPtr patterns;/* the patterns found */
 | |
|     xmlSchematronRulePtr rules;	/* the rules gathered */
 | |
|     int nbNamespaces;		/* number of namespaces in the array */
 | |
|     int maxNamespaces;		/* size of the array */
 | |
|     const xmlChar **namespaces;	/* the array of namespaces */
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronValidCtxt:
 | |
|  *
 | |
|  * A Schematrons validation context
 | |
|  */
 | |
| struct _xmlSchematronValidCtxt {
 | |
|     int type;
 | |
|     int flags;			/* an or of xmlSchematronValidOptions */
 | |
| 
 | |
|     xmlDictPtr dict;
 | |
|     int nberrors;
 | |
|     int err;
 | |
| 
 | |
|     xmlSchematronPtr schema;
 | |
|     xmlXPathContextPtr xctxt;
 | |
| 
 | |
|     FILE *outputFile;		/* if using XML_SCHEMATRON_OUT_FILE */
 | |
|     xmlBufferPtr outputBuffer;	/* if using XML_SCHEMATRON_OUT_BUFFER */
 | |
|     xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
 | |
|     xmlOutputCloseCallback  ioclose;
 | |
|     void *ioctx;
 | |
| 
 | |
|     /* error reporting data */
 | |
|     void *userData;                      /* user specific data block */
 | |
|     xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
 | |
|     xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
 | |
|     xmlStructuredErrorFunc serror;       /* the structured function */
 | |
| };
 | |
| 
 | |
| struct _xmlSchematronParserCtxt {
 | |
|     int type;
 | |
|     const xmlChar *URL;
 | |
|     xmlDocPtr doc;
 | |
|     int preserve;               /* Whether the doc should be freed  */
 | |
|     const char *buffer;
 | |
|     int size;
 | |
| 
 | |
|     xmlDictPtr dict;            /* dictionnary for interned string names */
 | |
| 
 | |
|     int nberrors;
 | |
|     int err;
 | |
|     xmlXPathContextPtr xctxt;	/* the XPath context used for compilation */
 | |
|     xmlSchematronPtr schema;
 | |
| 
 | |
|     int nbNamespaces;		/* number of namespaces in the array */
 | |
|     int maxNamespaces;		/* size of the array */
 | |
|     const xmlChar **namespaces;	/* the array of namespaces */
 | |
| 
 | |
|     int nbIncludes;		/* number of includes in the array */
 | |
|     int maxIncludes;		/* size of the array */
 | |
|     xmlNodePtr *includes;	/* the array of includes */
 | |
| 
 | |
|     /* error reporting data */
 | |
|     void *userData;                      /* user specific data block */
 | |
|     xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
 | |
|     xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
 | |
|     xmlStructuredErrorFunc serror;       /* the structured function */
 | |
| };
 | |
| 
 | |
| #define XML_STRON_CTXT_PARSER 1
 | |
| #define XML_STRON_CTXT_VALIDATOR 2
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Error reporting					*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronPErrMemory:
 | |
|  * @node: a context node
 | |
|  * @extra:  extra informations
 | |
|  *
 | |
|  * Handle an out of memory condition
 | |
|  */
 | |
| static void
 | |
| xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
 | |
|                         const char *extra, xmlNodePtr node)
 | |
| {
 | |
|     if (ctxt != NULL)
 | |
|         ctxt->nberrors++;
 | |
|     __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
 | |
|                      extra);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronPErr:
 | |
|  * @ctxt: the parsing context
 | |
|  * @node: the context node
 | |
|  * @error: the error code
 | |
|  * @msg: the error message
 | |
|  * @str1: extra data
 | |
|  * @str2: extra data
 | |
|  *
 | |
|  * Handle a parser error
 | |
|  */
 | |
| static void
 | |
| xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
 | |
|               const char *msg, const xmlChar * str1, const xmlChar * str2)
 | |
| {
 | |
|     xmlGenericErrorFunc channel = NULL;
 | |
|     xmlStructuredErrorFunc schannel = NULL;
 | |
|     void *data = NULL;
 | |
| 
 | |
|     if (ctxt != NULL) {
 | |
|         ctxt->nberrors++;
 | |
|         channel = ctxt->error;
 | |
|         data = ctxt->userData;
 | |
| 	schannel = ctxt->serror;
 | |
|     }
 | |
|     __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
 | |
|                     error, XML_ERR_ERROR, NULL, 0,
 | |
|                     (const char *) str1, (const char *) str2, NULL, 0, 0,
 | |
|                     msg, str1, str2);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronVTypeErrMemory:
 | |
|  * @node: a context node
 | |
|  * @extra:  extra informations
 | |
|  *
 | |
|  * Handle an out of memory condition
 | |
|  */
 | |
| static void
 | |
| xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
 | |
|                         const char *extra, xmlNodePtr node)
 | |
| {
 | |
|     if (ctxt != NULL) {
 | |
|         ctxt->nberrors++;
 | |
|         ctxt->err = XML_SCHEMAV_INTERNAL;
 | |
|     }
 | |
|     __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
 | |
|                      extra);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *		Parsing and compilation of the Schematrontrons		*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronAddTest:
 | |
|  * @ctxt: the schema parsing context
 | |
|  * @type:  the type of test
 | |
|  * @rule:  the parent rule
 | |
|  * @node:  the node hosting the test
 | |
|  * @test: the associated test
 | |
|  * @report: the associated report string
 | |
|  *
 | |
|  * Add a test to a schematron
 | |
|  *
 | |
|  * Returns the new pointer or NULL in case of error
 | |
|  */
 | |
| static xmlSchematronTestPtr
 | |
| xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
 | |
|                      xmlSchematronTestType type,
 | |
|                      xmlSchematronRulePtr rule,
 | |
|                      xmlNodePtr node, xmlChar *test, xmlChar *report)
 | |
| {
 | |
|     xmlSchematronTestPtr ret;
 | |
|     xmlXPathCompExprPtr comp;
 | |
| 
 | |
|     if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
 | |
|         (test == NULL))
 | |
|         return(NULL);
 | |
| 
 | |
|     /*
 | |
|      * try first to compile the test expression
 | |
|      */
 | |
|     comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
 | |
|     if (comp == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, node,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "Failed to compile test expression %s",
 | |
| 	    test, NULL);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronTest));
 | |
|     ret->type = type;
 | |
|     ret->node = node;
 | |
|     ret->test = test;
 | |
|     ret->comp = comp;
 | |
|     ret->report = report;
 | |
|     ret->next = NULL;
 | |
|     if (rule->tests == NULL) {
 | |
| 	rule->tests = ret;
 | |
|     } else {
 | |
|         xmlSchematronTestPtr prev = rule->tests;
 | |
| 
 | |
| 	while (prev->next != NULL)
 | |
| 	     prev = prev->next;
 | |
|         prev->next = ret;
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFreeTests:
 | |
|  * @tests:  a list of tests
 | |
|  *
 | |
|  * Free a list of tests.
 | |
|  */
 | |
| static void
 | |
| xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
 | |
|     xmlSchematronTestPtr next;
 | |
| 
 | |
|     while (tests != NULL) {
 | |
|         next = tests->next;
 | |
| 	if (tests->test != NULL)
 | |
| 	    xmlFree(tests->test);
 | |
| 	if (tests->comp != NULL)
 | |
| 	    xmlXPathFreeCompExpr(tests->comp);
 | |
| 	if (tests->report != NULL)
 | |
| 	    xmlFree(tests->report);
 | |
| 	xmlFree(tests);
 | |
| 	tests = next;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronAddRule:
 | |
|  * @ctxt: the schema parsing context
 | |
|  * @schema:  a schema structure
 | |
|  * @node:  the node hosting the rule
 | |
|  * @context: the associated context string
 | |
|  * @report: the associated report string
 | |
|  *
 | |
|  * Add a rule to a schematron
 | |
|  *
 | |
|  * Returns the new pointer or NULL in case of error
 | |
|  */
 | |
| static xmlSchematronRulePtr
 | |
| xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
 | |
|                      xmlSchematronPatternPtr pat, xmlNodePtr node,
 | |
| 		     xmlChar *context, xmlChar *report)
 | |
| {
 | |
|     xmlSchematronRulePtr ret;
 | |
|     xmlPatternPtr pattern;
 | |
| 
 | |
|     if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
 | |
|         (context == NULL))
 | |
|         return(NULL);
 | |
| 
 | |
|     /*
 | |
|      * Try first to compile the pattern
 | |
|      */
 | |
|     pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
 | |
|                                 ctxt->namespaces);
 | |
|     if (pattern == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, node,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "Failed to compile context expression %s",
 | |
| 	    context, NULL);
 | |
|     }
 | |
| 
 | |
|     ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronRule));
 | |
|     ret->node = node;
 | |
|     ret->context = context;
 | |
|     ret->pattern = pattern;
 | |
|     ret->report = report;
 | |
|     ret->next = NULL;
 | |
|     if (schema->rules == NULL) {
 | |
| 	schema->rules = ret;
 | |
|     } else {
 | |
|         xmlSchematronRulePtr prev = schema->rules;
 | |
| 
 | |
| 	while (prev->next != NULL)
 | |
| 	     prev = prev->next;
 | |
|         prev->next = ret;
 | |
|     }
 | |
|     ret->patnext = NULL;
 | |
|     if (pat->rules == NULL) {
 | |
| 	pat->rules = ret;
 | |
|     } else {
 | |
|         xmlSchematronRulePtr prev = pat->rules;
 | |
| 
 | |
| 	while (prev->patnext != NULL)
 | |
| 	     prev = prev->patnext;
 | |
|         prev->patnext = ret;
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFreeRules:
 | |
|  * @rules:  a list of rules
 | |
|  *
 | |
|  * Free a list of rules.
 | |
|  */
 | |
| static void
 | |
| xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
 | |
|     xmlSchematronRulePtr next;
 | |
| 
 | |
|     while (rules != NULL) {
 | |
|         next = rules->next;
 | |
| 	if (rules->tests)
 | |
| 	    xmlSchematronFreeTests(rules->tests);
 | |
| 	if (rules->context != NULL)
 | |
| 	    xmlFree(rules->context);
 | |
| 	if (rules->pattern)
 | |
| 	    xmlFreePattern(rules->pattern);
 | |
| 	if (rules->report != NULL)
 | |
| 	    xmlFree(rules->report);
 | |
| 	xmlFree(rules);
 | |
| 	rules = next;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronAddPattern:
 | |
|  * @ctxt: the schema parsing context
 | |
|  * @schema:  a schema structure
 | |
|  * @node:  the node hosting the pattern
 | |
|  * @id: the id or name of the pattern
 | |
|  *
 | |
|  * Add a pattern to a schematron
 | |
|  *
 | |
|  * Returns the new pointer or NULL in case of error
 | |
|  */
 | |
| static xmlSchematronPatternPtr
 | |
| xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
 | |
|                      xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
 | |
| {
 | |
|     xmlSchematronPatternPtr ret;
 | |
| 
 | |
|     if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
 | |
|         return(NULL);
 | |
| 
 | |
|     ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronPattern));
 | |
|     ret->name = name;
 | |
|     ret->next = NULL;
 | |
|     if (schema->patterns == NULL) {
 | |
| 	schema->patterns = ret;
 | |
|     } else {
 | |
|         xmlSchematronPatternPtr prev = schema->patterns;
 | |
| 
 | |
| 	while (prev->next != NULL)
 | |
| 	     prev = prev->next;
 | |
|         prev->next = ret;
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFreePatterns:
 | |
|  * @patterns:  a list of patterns
 | |
|  *
 | |
|  * Free a list of patterns.
 | |
|  */
 | |
| static void
 | |
| xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
 | |
|     xmlSchematronPatternPtr next;
 | |
| 
 | |
|     while (patterns != NULL) {
 | |
|         next = patterns->next;
 | |
| 	if (patterns->name != NULL)
 | |
| 	    xmlFree(patterns->name);
 | |
| 	xmlFree(patterns);
 | |
| 	patterns = next;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronNewSchematron:
 | |
|  * @ctxt:  a schema validation context
 | |
|  *
 | |
|  * Allocate a new Schematron structure.
 | |
|  *
 | |
|  * Returns the newly allocated structure or NULL in case or error
 | |
|  */
 | |
| static xmlSchematronPtr
 | |
| xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
 | |
| {
 | |
|     xmlSchematronPtr ret;
 | |
| 
 | |
|     ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematron));
 | |
|     ret->dict = ctxt->dict;
 | |
|     xmlDictReference(ret->dict);
 | |
| 
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFree:
 | |
|  * @schema:  a schema structure
 | |
|  *
 | |
|  * Deallocate a Schematron structure.
 | |
|  */
 | |
| void
 | |
| xmlSchematronFree(xmlSchematronPtr schema)
 | |
| {
 | |
|     if (schema == NULL)
 | |
|         return;
 | |
| 
 | |
|     if ((schema->doc != NULL) && (!(schema->preserve)))
 | |
|         xmlFreeDoc(schema->doc);
 | |
| 
 | |
|     if (schema->namespaces != NULL)
 | |
|         xmlFree((char **) schema->namespaces);
 | |
| 
 | |
|     xmlSchematronFreeRules(schema->rules);
 | |
|     xmlSchematronFreePatterns(schema->patterns);
 | |
|     xmlDictFree(schema->dict);
 | |
|     xmlFree(schema);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronNewParserCtxt:
 | |
|  * @URL:  the location of the schema
 | |
|  *
 | |
|  * Create an XML Schematrons parse context for that file/resource expected
 | |
|  * to contain an XML Schematrons file.
 | |
|  *
 | |
|  * Returns the parser context or NULL in case of error
 | |
|  */
 | |
| xmlSchematronParserCtxtPtr
 | |
| xmlSchematronNewParserCtxt(const char *URL)
 | |
| {
 | |
|     xmlSchematronParserCtxtPtr ret;
 | |
| 
 | |
|     if (URL == NULL)
 | |
|         return (NULL);
 | |
| 
 | |
|     ret =
 | |
|         (xmlSchematronParserCtxtPtr)
 | |
|         xmlMalloc(sizeof(xmlSchematronParserCtxt));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser context",
 | |
|                                 NULL);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronParserCtxt));
 | |
|     ret->type = XML_STRON_CTXT_PARSER;
 | |
|     ret->dict = xmlDictCreate();
 | |
|     ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
 | |
|     ret->includes = NULL;
 | |
|     ret->xctxt = xmlXPathNewContext(NULL);
 | |
|     if (ret->xctxt == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
 | |
|                                 NULL);
 | |
| 	xmlSchematronFreeParserCtxt(ret);
 | |
|         return (NULL);
 | |
|     }
 | |
|     ret->xctxt->flags = XML_XPATH_CHECKNS;
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronNewMemParserCtxt:
 | |
|  * @buffer:  a pointer to a char array containing the schemas
 | |
|  * @size:  the size of the array
 | |
|  *
 | |
|  * Create an XML Schematrons parse context for that memory buffer expected
 | |
|  * to contain an XML Schematrons file.
 | |
|  *
 | |
|  * Returns the parser context or NULL in case of error
 | |
|  */
 | |
| xmlSchematronParserCtxtPtr
 | |
| xmlSchematronNewMemParserCtxt(const char *buffer, int size)
 | |
| {
 | |
|     xmlSchematronParserCtxtPtr ret;
 | |
| 
 | |
|     if ((buffer == NULL) || (size <= 0))
 | |
|         return (NULL);
 | |
| 
 | |
|     ret =
 | |
|         (xmlSchematronParserCtxtPtr)
 | |
|         xmlMalloc(sizeof(xmlSchematronParserCtxt));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser context",
 | |
|                                 NULL);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronParserCtxt));
 | |
|     ret->buffer = buffer;
 | |
|     ret->size = size;
 | |
|     ret->dict = xmlDictCreate();
 | |
|     ret->xctxt = xmlXPathNewContext(NULL);
 | |
|     if (ret->xctxt == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
 | |
|                                 NULL);
 | |
| 	xmlSchematronFreeParserCtxt(ret);
 | |
|         return (NULL);
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronNewDocParserCtxt:
 | |
|  * @doc:  a preparsed document tree
 | |
|  *
 | |
|  * Create an XML Schematrons parse context for that document.
 | |
|  * NB. The document may be modified during the parsing process.
 | |
|  *
 | |
|  * Returns the parser context or NULL in case of error
 | |
|  */
 | |
| xmlSchematronParserCtxtPtr
 | |
| xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
 | |
| {
 | |
|     xmlSchematronParserCtxtPtr ret;
 | |
| 
 | |
|     if (doc == NULL)
 | |
|         return (NULL);
 | |
| 
 | |
|     ret =
 | |
|         (xmlSchematronParserCtxtPtr)
 | |
|         xmlMalloc(sizeof(xmlSchematronParserCtxt));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser context",
 | |
|                                 NULL);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronParserCtxt));
 | |
|     ret->doc = doc;
 | |
|     ret->dict = xmlDictCreate();
 | |
|     /* The application has responsibility for the document */
 | |
|     ret->preserve = 1;
 | |
|     ret->xctxt = xmlXPathNewContext(doc);
 | |
|     if (ret->xctxt == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
 | |
|                                 NULL);
 | |
| 	xmlSchematronFreeParserCtxt(ret);
 | |
|         return (NULL);
 | |
|     }
 | |
| 
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFreeParserCtxt:
 | |
|  * @ctxt:  the schema parser context
 | |
|  *
 | |
|  * Free the resources associated to the schema parser context
 | |
|  */
 | |
| void
 | |
| xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
 | |
| {
 | |
|     if (ctxt == NULL)
 | |
|         return;
 | |
|     if (ctxt->doc != NULL && !ctxt->preserve)
 | |
|         xmlFreeDoc(ctxt->doc);
 | |
|     if (ctxt->xctxt != NULL) {
 | |
|         xmlXPathFreeContext(ctxt->xctxt);
 | |
|     }
 | |
|     if (ctxt->namespaces != NULL)
 | |
|         xmlFree((char **) ctxt->namespaces);
 | |
|     xmlDictFree(ctxt->dict);
 | |
|     xmlFree(ctxt);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| /**
 | |
|  * xmlSchematronPushInclude:
 | |
|  * @ctxt:  the schema parser context
 | |
|  * @doc:  the included document
 | |
|  * @cur:  the current include node
 | |
|  *
 | |
|  * Add an included document
 | |
|  */
 | |
| static void
 | |
| xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
 | |
|                         xmlDocPtr doc, xmlNodePtr cur)
 | |
| {
 | |
|     if (ctxt->includes == NULL) {
 | |
|         ctxt->maxIncludes = 10;
 | |
|         ctxt->includes = (xmlNodePtr *)
 | |
| 	    xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
 | |
| 	if (ctxt->includes == NULL) {
 | |
| 	    xmlSchematronPErrMemory(NULL, "allocating parser includes",
 | |
| 				    NULL);
 | |
| 	    return;
 | |
| 	}
 | |
|         ctxt->nbIncludes = 0;
 | |
|     } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
 | |
|         xmlNodePtr *tmp;
 | |
| 
 | |
| 	tmp = (xmlNodePtr *)
 | |
| 	    xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
 | |
| 	               sizeof(xmlNodePtr));
 | |
| 	if (tmp == NULL) {
 | |
| 	    xmlSchematronPErrMemory(NULL, "allocating parser includes",
 | |
| 				    NULL);
 | |
| 	    return;
 | |
| 	}
 | |
|         ctxt->includes = tmp;
 | |
| 	ctxt->maxIncludes *= 2;
 | |
|     }
 | |
|     ctxt->includes[2 * ctxt->nbIncludes] = cur;
 | |
|     ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
 | |
|     ctxt->nbIncludes++;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronPopInclude:
 | |
|  * @ctxt:  the schema parser context
 | |
|  *
 | |
|  * Pop an include level. The included document is being freed
 | |
|  *
 | |
|  * Returns the node immediately following the include or NULL if the
 | |
|  *         include list was empty.
 | |
|  */
 | |
| static xmlNodePtr
 | |
| xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
 | |
| {
 | |
|     xmlDocPtr doc;
 | |
|     xmlNodePtr ret;
 | |
| 
 | |
|     if (ctxt->nbIncludes <= 0)
 | |
|         return(NULL);
 | |
|     ctxt->nbIncludes--;
 | |
|     doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
 | |
|     ret = ctxt->includes[2 * ctxt->nbIncludes];
 | |
|     xmlFreeDoc(doc);
 | |
|     if (ret != NULL)
 | |
| 	ret = ret->next;
 | |
|     if (ret == NULL)
 | |
|         return(xmlSchematronPopInclude(ctxt));
 | |
|     return(ret);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronAddNamespace:
 | |
|  * @ctxt:  the schema parser context
 | |
|  * @prefix:  the namespace prefix
 | |
|  * @ns:  the namespace name
 | |
|  *
 | |
|  * Add a namespace definition in the context
 | |
|  */
 | |
| static void
 | |
| xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
 | |
|                           const xmlChar *prefix, const xmlChar *ns)
 | |
| {
 | |
|     if (ctxt->namespaces == NULL) {
 | |
|         ctxt->maxNamespaces = 10;
 | |
|         ctxt->namespaces = (const xmlChar **)
 | |
| 	    xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
 | |
| 	if (ctxt->namespaces == NULL) {
 | |
| 	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
 | |
| 				    NULL);
 | |
| 	    return;
 | |
| 	}
 | |
|         ctxt->nbNamespaces = 0;
 | |
|     } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
 | |
|         const xmlChar **tmp;
 | |
| 
 | |
| 	tmp = (const xmlChar **)
 | |
| 	    xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
 | |
| 	               sizeof(const xmlChar *));
 | |
| 	if (tmp == NULL) {
 | |
| 	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
 | |
| 				    NULL);
 | |
| 	    return;
 | |
| 	}
 | |
|         ctxt->namespaces = tmp;
 | |
| 	ctxt->maxNamespaces *= 2;
 | |
|     }
 | |
|     ctxt->namespaces[2 * ctxt->nbNamespaces] =
 | |
|         xmlDictLookup(ctxt->dict, ns, -1);
 | |
|     ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
 | |
|         xmlDictLookup(ctxt->dict, prefix, -1);
 | |
|     ctxt->nbNamespaces++;
 | |
|     ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
 | |
|     ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronParseRule:
 | |
|  * @ctxt:  a schema validation context
 | |
|  * @rule:  the rule node
 | |
|  *
 | |
|  * parse a rule element
 | |
|  */
 | |
| static void
 | |
| xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
 | |
|                        xmlSchematronPatternPtr pattern,
 | |
| 		       xmlNodePtr rule)
 | |
| {
 | |
|     xmlNodePtr cur;
 | |
|     int nbChecks = 0;
 | |
|     xmlChar *test;
 | |
|     xmlChar *context;
 | |
|     xmlChar *report;
 | |
|     xmlSchematronRulePtr ruleptr;
 | |
|     xmlSchematronTestPtr testptr;
 | |
| 
 | |
|     if ((ctxt == NULL) || (rule == NULL)) return;
 | |
| 
 | |
|     context = xmlGetNoNsProp(rule, BAD_CAST "context");
 | |
|     if (context == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, rule,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "rule has no context attribute",
 | |
| 	    NULL, NULL);
 | |
| 	return;
 | |
|     } else if (context[0] == 0) {
 | |
| 	xmlSchematronPErr(ctxt, rule,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "rule has an empty context attribute",
 | |
| 	    NULL, NULL);
 | |
| 	xmlFree(context);
 | |
| 	return;
 | |
|     } else {
 | |
| 	ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
 | |
| 	                               rule, context, NULL);
 | |
| 	if (ruleptr == NULL) {
 | |
| 	    xmlFree(context);
 | |
| 	    return;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     cur = rule->children;
 | |
|     NEXT_SCHEMATRON(cur);
 | |
|     while (cur != NULL) {
 | |
| 	if (IS_SCHEMATRON(cur, "assert")) {
 | |
| 	    nbChecks++;
 | |
| 	    test = xmlGetNoNsProp(cur, BAD_CAST "test");
 | |
| 	    if (test == NULL) {
 | |
| 		xmlSchematronPErr(ctxt, cur,
 | |
| 		    XML_SCHEMAP_NOROOT,
 | |
| 		    "assert has no test attribute",
 | |
| 		    NULL, NULL);
 | |
| 	    } else if (test[0] == 0) {
 | |
| 		xmlSchematronPErr(ctxt, cur,
 | |
| 		    XML_SCHEMAP_NOROOT,
 | |
| 		    "assert has an empty test attribute",
 | |
| 		    NULL, NULL);
 | |
| 		xmlFree(test);
 | |
| 	    } else {
 | |
| 		/* TODO will need dynamic processing instead */
 | |
| 		report = xmlNodeGetContent(cur);
 | |
| 
 | |
| 		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
 | |
| 		                               ruleptr, cur, test, report);
 | |
| 		if (testptr == NULL)
 | |
| 		    xmlFree(test);
 | |
| 	    }
 | |
| 	} else if (IS_SCHEMATRON(cur, "report")) {
 | |
| 	    nbChecks++;
 | |
| 	    test = xmlGetNoNsProp(cur, BAD_CAST "test");
 | |
| 	    if (test == NULL) {
 | |
| 		xmlSchematronPErr(ctxt, cur,
 | |
| 		    XML_SCHEMAP_NOROOT,
 | |
| 		    "assert has no test attribute",
 | |
| 		    NULL, NULL);
 | |
| 	    } else if (test[0] == 0) {
 | |
| 		xmlSchematronPErr(ctxt, cur,
 | |
| 		    XML_SCHEMAP_NOROOT,
 | |
| 		    "assert has an empty test attribute",
 | |
| 		    NULL, NULL);
 | |
| 		xmlFree(test);
 | |
| 	    } else {
 | |
| 		/* TODO will need dynamic processing instead */
 | |
| 		report = xmlNodeGetContent(cur);
 | |
| 
 | |
| 		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
 | |
| 		                               ruleptr, cur, test, report);
 | |
| 		if (testptr == NULL)
 | |
| 		    xmlFree(test);
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    xmlSchematronPErr(ctxt, cur,
 | |
| 		XML_SCHEMAP_NOROOT,
 | |
| 		"Expecting an assert or a report element instead of %s",
 | |
| 		cur->name, NULL);
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
| 	NEXT_SCHEMATRON(cur);
 | |
|     }
 | |
|     if (nbChecks == 0) {
 | |
| 	xmlSchematronPErr(ctxt, rule,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "rule has no assert nor report element", NULL, NULL);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronParsePattern:
 | |
|  * @ctxt:  a schema validation context
 | |
|  * @pat:  the pattern node
 | |
|  *
 | |
|  * parse a pattern element
 | |
|  */
 | |
| static void
 | |
| xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
 | |
| {
 | |
|     xmlNodePtr cur;
 | |
|     xmlSchematronPatternPtr pattern;
 | |
|     int nbRules = 0;
 | |
|     xmlChar *id;
 | |
| 
 | |
|     if ((ctxt == NULL) || (pat == NULL)) return;
 | |
| 
 | |
|     id = xmlGetNoNsProp(pat, BAD_CAST "id");
 | |
|     if (id == NULL) {
 | |
| 	id = xmlGetNoNsProp(pat, BAD_CAST "name");
 | |
|     }
 | |
|     pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
 | |
|     if (pattern == NULL) {
 | |
| 	if (id != NULL)
 | |
| 	    xmlFree(id);
 | |
|         return;
 | |
|     }
 | |
|     cur = pat->children;
 | |
|     NEXT_SCHEMATRON(cur);
 | |
|     while (cur != NULL) {
 | |
| 	if (IS_SCHEMATRON(cur, "rule")) {
 | |
| 	    xmlSchematronParseRule(ctxt, pattern, cur);
 | |
| 	    nbRules++;
 | |
| 	} else {
 | |
| 	    xmlSchematronPErr(ctxt, cur,
 | |
| 		XML_SCHEMAP_NOROOT,
 | |
| 		"Expecting a rule element instead of %s", cur->name, NULL);
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
| 	NEXT_SCHEMATRON(cur);
 | |
|     }
 | |
|     if (nbRules == 0) {
 | |
| 	xmlSchematronPErr(ctxt, pat,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "Pattern has no rule element", NULL, NULL);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| /**
 | |
|  * xmlSchematronLoadInclude:
 | |
|  * @ctxt:  a schema validation context
 | |
|  * @cur:  the include element
 | |
|  *
 | |
|  * Load the include document, Push the current pointer
 | |
|  *
 | |
|  * Returns the updated node pointer
 | |
|  */
 | |
| static xmlNodePtr
 | |
| xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
 | |
| {
 | |
|     xmlNodePtr ret = NULL;
 | |
|     xmlDocPtr doc = NULL;
 | |
|     xmlChar *href = NULL;
 | |
|     xmlChar *base = NULL;
 | |
|     xmlChar *URI = NULL;
 | |
| 
 | |
|     if ((ctxt == NULL) || (cur == NULL))
 | |
|         return(NULL);
 | |
| 
 | |
|     href = xmlGetNoNsProp(cur, BAD_CAST "href");
 | |
|     if (href == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, cur,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "Include has no href attribute", NULL, NULL);
 | |
| 	return(cur->next);
 | |
|     }
 | |
| 
 | |
|     /* do the URI base composition, load and find the root */
 | |
|     base = xmlNodeGetBase(cur->doc, cur);
 | |
|     URI = xmlBuildURI(href, base);
 | |
|     doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
 | |
|     if (doc == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, cur,
 | |
| 		      XML_SCHEMAP_FAILED_LOAD,
 | |
| 		      "could not load include '%s'.\n",
 | |
| 		      URI, NULL);
 | |
| 	goto done;
 | |
|     }
 | |
|     ret = xmlDocGetRootElement(doc);
 | |
|     if (ret == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, cur,
 | |
| 		      XML_SCHEMAP_FAILED_LOAD,
 | |
| 		      "could not find root from include '%s'.\n",
 | |
| 		      URI, NULL);
 | |
| 	goto done;
 | |
|     }
 | |
| 
 | |
|     /* Success, push the include for rollback on exit */
 | |
|     xmlSchematronPushInclude(ctxt, doc, cur);
 | |
| 
 | |
| done:
 | |
|     if (ret == NULL) {
 | |
|         if (doc != NULL)
 | |
| 	    xmlFreeDoc(doc);
 | |
|     }
 | |
|     xmlFree(href);
 | |
|     if (base != NULL)
 | |
|         xmlFree(base);
 | |
|     if (URI != NULL)
 | |
|         xmlFree(URI);
 | |
|     return(ret);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronParse:
 | |
|  * @ctxt:  a schema validation context
 | |
|  *
 | |
|  * parse a schema definition resource and build an internal
 | |
|  * XML Shema struture which can be used to validate instances.
 | |
|  *
 | |
|  * Returns the internal XML Schematron structure built from the resource or
 | |
|  *         NULL in case of error
 | |
|  */
 | |
| xmlSchematronPtr
 | |
| xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
 | |
| {
 | |
|     xmlSchematronPtr ret = NULL;
 | |
|     xmlDocPtr doc;
 | |
|     xmlNodePtr root, cur;
 | |
|     int preserve = 0;
 | |
| 
 | |
|     if (ctxt == NULL)
 | |
|         return (NULL);
 | |
| 
 | |
|     ctxt->nberrors = 0;
 | |
| 
 | |
|     /*
 | |
|      * First step is to parse the input document into an DOM/Infoset
 | |
|      */
 | |
|     if (ctxt->URL != NULL) {
 | |
|         doc = xmlReadFile((const char *) ctxt->URL, NULL,
 | |
| 	                  SCHEMATRON_PARSE_OPTIONS);
 | |
|         if (doc == NULL) {
 | |
| 	    xmlSchematronPErr(ctxt, NULL,
 | |
| 			  XML_SCHEMAP_FAILED_LOAD,
 | |
|                           "xmlSchematronParse: could not load '%s'.\n",
 | |
|                           ctxt->URL, NULL);
 | |
|             return (NULL);
 | |
|         }
 | |
| 	ctxt->preserve = 0;
 | |
|     } else if (ctxt->buffer != NULL) {
 | |
|         doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
 | |
| 	                    SCHEMATRON_PARSE_OPTIONS);
 | |
|         if (doc == NULL) {
 | |
| 	    xmlSchematronPErr(ctxt, NULL,
 | |
| 			  XML_SCHEMAP_FAILED_PARSE,
 | |
|                           "xmlSchematronParse: could not parse.\n",
 | |
|                           NULL, NULL);
 | |
|             return (NULL);
 | |
|         }
 | |
|         doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
 | |
|         ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
 | |
| 	ctxt->preserve = 0;
 | |
|     } else if (ctxt->doc != NULL) {
 | |
|         doc = ctxt->doc;
 | |
| 	preserve = 1;
 | |
| 	ctxt->preserve = 1;
 | |
|     } else {
 | |
| 	xmlSchematronPErr(ctxt, NULL,
 | |
| 		      XML_SCHEMAP_NOTHING_TO_PARSE,
 | |
| 		      "xmlSchematronParse: could not parse.\n",
 | |
| 		      NULL, NULL);
 | |
|         return (NULL);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Then extract the root and Schematron parse it
 | |
|      */
 | |
|     root = xmlDocGetRootElement(doc);
 | |
|     if (root == NULL) {
 | |
| 	xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
 | |
| 		      XML_SCHEMAP_NOROOT,
 | |
| 		      "The schema has no document element.\n", NULL, NULL);
 | |
| 	if (!preserve) {
 | |
| 	    xmlFreeDoc(doc);
 | |
| 	}
 | |
|         return (NULL);
 | |
|     }
 | |
| 
 | |
|     if (!IS_SCHEMATRON(root, "schema")) {
 | |
| 	xmlSchematronPErr(ctxt, root,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "The XML document '%s' is not a XML schematron document",
 | |
| 	    ctxt->URL, NULL);
 | |
| 	goto exit;
 | |
|     }
 | |
|     ret = xmlSchematronNewSchematron(ctxt);
 | |
|     if (ret == NULL)
 | |
|         goto exit;
 | |
|     ctxt->schema = ret;
 | |
| 
 | |
|     /*
 | |
|      * scan the schema elements
 | |
|      */
 | |
|     cur = root->children;
 | |
|     NEXT_SCHEMATRON(cur);
 | |
|     if (IS_SCHEMATRON(cur, "title")) {
 | |
|         xmlChar *title = xmlNodeGetContent(cur);
 | |
| 	if (title != NULL) {
 | |
| 	    ret->title = xmlDictLookup(ret->dict, title, -1);
 | |
| 	    xmlFree(title);
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
| 	NEXT_SCHEMATRON(cur);
 | |
|     }
 | |
|     while (IS_SCHEMATRON(cur, "ns")) {
 | |
|         xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
 | |
|         xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
 | |
| 	if ((uri == NULL) || (uri[0] == 0)) {
 | |
| 	    xmlSchematronPErr(ctxt, cur,
 | |
| 		XML_SCHEMAP_NOROOT,
 | |
| 		"ns element has no uri", NULL, NULL);
 | |
| 	}
 | |
| 	if ((prefix == NULL) || (prefix[0] == 0)) {
 | |
| 	    xmlSchematronPErr(ctxt, cur,
 | |
| 		XML_SCHEMAP_NOROOT,
 | |
| 		"ns element has no prefix", NULL, NULL);
 | |
| 	}
 | |
| 	if ((prefix) && (uri)) {
 | |
| 	    xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
 | |
| 	    xmlSchematronAddNamespace(ctxt, prefix, uri);
 | |
| 	    ret->nbNs++;
 | |
| 	}
 | |
| 	if (uri)
 | |
| 	    xmlFree(uri);
 | |
| 	if (prefix)
 | |
| 	    xmlFree(prefix);
 | |
| 	cur = cur->next;
 | |
| 	NEXT_SCHEMATRON(cur);
 | |
|     }
 | |
|     while (cur != NULL) {
 | |
| 	if (IS_SCHEMATRON(cur, "pattern")) {
 | |
| 	    xmlSchematronParsePattern(ctxt, cur);
 | |
| 	    ret->nbPattern++;
 | |
| 	} else {
 | |
| 	    xmlSchematronPErr(ctxt, cur,
 | |
| 		XML_SCHEMAP_NOROOT,
 | |
| 		"Expecting a pattern element instead of %s", cur->name, NULL);
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
| 	NEXT_SCHEMATRON(cur);
 | |
|     }
 | |
|     if (ret->nbPattern == 0) {
 | |
| 	xmlSchematronPErr(ctxt, root,
 | |
| 	    XML_SCHEMAP_NOROOT,
 | |
| 	    "The schematron document '%s' has no pattern",
 | |
| 	    ctxt->URL, NULL);
 | |
| 	goto exit;
 | |
|     }
 | |
|     /* the original document must be kept for reporting */
 | |
|     ret->doc = doc;
 | |
|     if (preserve) {
 | |
| 	    ret->preserve = 1;
 | |
|     }
 | |
|     preserve = 1;
 | |
| 
 | |
| exit:
 | |
|     if (!preserve) {
 | |
| 	xmlFreeDoc(doc);
 | |
|     }
 | |
|     if (ret != NULL) {
 | |
| 	if (ctxt->nberrors != 0) {
 | |
| 	    xmlSchematronFree(ret);
 | |
| 	    ret = NULL;
 | |
| 	} else {
 | |
| 	    ret->namespaces = ctxt->namespaces;
 | |
| 	    ret->nbNamespaces = ctxt->nbNamespaces;
 | |
| 	    ctxt->namespaces = NULL;
 | |
| 	}
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *		Schematrontron Reports handler				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| static xmlNodePtr
 | |
| xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
 | |
|                      xmlNodePtr cur, const xmlChar *xpath) {
 | |
|     xmlNodePtr node = NULL;
 | |
|     xmlXPathObjectPtr ret;
 | |
| 
 | |
|     if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
 | |
|         return(NULL);
 | |
| 
 | |
|     ctxt->xctxt->doc = cur->doc;
 | |
|     ctxt->xctxt->node = cur;
 | |
|     ret = xmlXPathEval(xpath, ctxt->xctxt);
 | |
|     if (ret == NULL)
 | |
|         return(NULL);
 | |
| 
 | |
|     if ((ret->type == XPATH_NODESET) &&
 | |
|         (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
 | |
| 	node = ret->nodesetval->nodeTab[0];
 | |
| 
 | |
|     xmlXPathFreeObject(ret);
 | |
|     return(node);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronReportOutput:
 | |
|  * @ctxt: the validation context
 | |
|  * @cur: the current node tested
 | |
|  * @msg: the message output
 | |
|  *
 | |
|  * Output part of the report to whatever channel the user selected
 | |
|  */
 | |
| static void
 | |
| xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
 | |
|                           xmlNodePtr cur ATTRIBUTE_UNUSED,
 | |
|                           const char *msg) {
 | |
|     /* TODO */
 | |
|     fprintf(stderr, "%s", msg);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFormatReport:
 | |
|  * @ctxt:  the validation context
 | |
|  * @test: the test node
 | |
|  * @cur: the current node tested
 | |
|  *
 | |
|  * Build the string being reported to the user.
 | |
|  *
 | |
|  * Returns a report string or NULL in case of error. The string needs
 | |
|  *         to be deallocated by teh caller
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
 | |
| 			  xmlNodePtr test, xmlNodePtr cur) {
 | |
|     xmlChar *ret = NULL;
 | |
|     xmlNodePtr child, node;
 | |
| 
 | |
|     if ((test == NULL) || (cur == NULL))
 | |
|         return(ret);
 | |
| 
 | |
|     child = test->children;
 | |
|     while (child != NULL) {
 | |
|         if ((child->type == XML_TEXT_NODE) ||
 | |
| 	    (child->type == XML_CDATA_SECTION_NODE))
 | |
| 	    ret = xmlStrcat(ret, child->content);
 | |
| 	else if (IS_SCHEMATRON(child, "name")) {
 | |
| 	    xmlChar *path;
 | |
| 
 | |
| 	    path = xmlGetNoNsProp(child, BAD_CAST "path");
 | |
| 
 | |
|             node = cur;
 | |
| 	    if (path != NULL) {
 | |
| 	        node = xmlSchematronGetNode(ctxt, cur, path);
 | |
| 		if (node == NULL)
 | |
| 		    node = cur;
 | |
| 		xmlFree(path);
 | |
| 	    }
 | |
| 
 | |
| 	    if ((node->ns == NULL) || (node->ns->prefix == NULL))
 | |
| 	        ret = xmlStrcat(ret, node->name);
 | |
| 	    else {
 | |
| 	        ret = xmlStrcat(ret, node->ns->prefix);
 | |
| 	        ret = xmlStrcat(ret, BAD_CAST ":");
 | |
| 	        ret = xmlStrcat(ret, node->name);
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    child = child->next;
 | |
| 	    continue;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * remove superfluous \n
 | |
| 	 */
 | |
| 	if (ret != NULL) {
 | |
| 	    int len = xmlStrlen(ret);
 | |
| 	    xmlChar c;
 | |
| 
 | |
| 	    if (len > 0) {
 | |
| 		c = ret[len - 1];
 | |
| 		if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
 | |
| 		    while ((c == ' ') || (c == '\n') ||
 | |
| 		           (c == '\r') || (c == '\t')) {
 | |
| 			len--;
 | |
| 			if (len == 0)
 | |
| 			    break;
 | |
| 			c = ret[len - 1];
 | |
| 		    }
 | |
| 		    ret[len] = ' ';
 | |
| 		    ret[len + 1] = 0;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|         child = child->next;
 | |
|     }
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronReportSuccess:
 | |
|  * @ctxt:  the validation context
 | |
|  * @test: the compiled test
 | |
|  * @cur: the current node tested
 | |
|  * @success: boolean value for the result
 | |
|  *
 | |
|  * called from the validation engine when an assert or report test have
 | |
|  * been done.
 | |
|  */
 | |
| static void
 | |
| xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
 | |
| 		   xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
 | |
|     if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
 | |
|         return;
 | |
|     /* if quiet and not SVRL report only failures */
 | |
|     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
 | |
|         ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
 | |
| 	(test->type == XML_SCHEMATRON_REPORT))
 | |
|         return;
 | |
|     if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
 | |
|         TODO
 | |
|     } else {
 | |
|         xmlChar *path;
 | |
| 	char msg[1000];
 | |
| 	long line;
 | |
| 	const xmlChar *report = NULL;
 | |
| 
 | |
|         if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
 | |
| 	    ((test->type == XML_SCHEMATRON_ASSERT) & (success)))
 | |
| 	    return;
 | |
| 	line = xmlGetLineNo(cur);
 | |
| 	path = xmlGetNodePath(cur);
 | |
| 	if (path == NULL)
 | |
| 	    path = (xmlChar *) cur->name;
 | |
| #if 0
 | |
| 	if ((test->report != NULL) && (test->report[0] != 0))
 | |
| 	    report = test->report;
 | |
| #endif
 | |
| 	if (test->node != NULL)
 | |
|             report = xmlSchematronFormatReport(ctxt, test->node, cur);
 | |
| 	if (report == NULL) {
 | |
| 	    if (test->type == XML_SCHEMATRON_ASSERT) {
 | |
|             report = xmlStrdup((const xmlChar *) "node failed assert");
 | |
| 	    } else {
 | |
|             report = xmlStrdup((const xmlChar *) "node failed report");
 | |
| 	    }
 | |
| 	    }
 | |
| 	    snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
 | |
| 		     line, (const char *) report);
 | |
| 
 | |
|     if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
 | |
|         xmlStructuredErrorFunc schannel = NULL;
 | |
|         xmlGenericErrorFunc channel = NULL;
 | |
|         void *data = NULL;
 | |
| 
 | |
|         if (ctxt != NULL) {
 | |
|             if (ctxt->serror != NULL)
 | |
|                 schannel = ctxt->serror;
 | |
|             else
 | |
|                 channel = ctxt->error;
 | |
|             data = ctxt->userData;
 | |
| 	}
 | |
| 
 | |
|         __xmlRaiseError(schannel, channel, data,
 | |
|                         NULL, cur, XML_FROM_SCHEMATRONV,
 | |
|                         (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,
 | |
|                         XML_ERR_ERROR, NULL, line,
 | |
|                         (pattern == NULL)?NULL:((const char *) pattern->name),
 | |
|                         (const char *) path,
 | |
|                         (const char *) report, 0, 0,
 | |
|                         "%s", msg);
 | |
|     } else {
 | |
| 	xmlSchematronReportOutput(ctxt, cur, &msg[0]);
 | |
|     }
 | |
| 
 | |
|     xmlFree((char *) report);
 | |
| 
 | |
| 	if ((path != NULL) && (path != (xmlChar *) cur->name))
 | |
| 	    xmlFree(path);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronReportPattern:
 | |
|  * @ctxt:  the validation context
 | |
|  * @pattern: the current pattern
 | |
|  *
 | |
|  * called from the validation engine when starting to check a pattern
 | |
|  */
 | |
| static void
 | |
| xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
 | |
| 			   xmlSchematronPatternPtr pattern) {
 | |
|     if ((ctxt == NULL) || (pattern == NULL))
 | |
|         return;
 | |
|     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
 | |
|         return;
 | |
|     if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
 | |
|         TODO
 | |
|     } else {
 | |
| 	char msg[1000];
 | |
| 
 | |
| 	if (pattern->name == NULL)
 | |
| 	    return;
 | |
| 	snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
 | |
| 	xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *		Validation against a Schematrontron				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronSetValidStructuredErrors:
 | |
|  * @ctxt:  a Schematron validation context
 | |
|  * @serror:  the structured error function
 | |
|  * @ctx: the functions context
 | |
|  *
 | |
|  * Set the structured error callback
 | |
|  */
 | |
| void
 | |
| xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
 | |
|                                       xmlStructuredErrorFunc serror, void *ctx)
 | |
| {
 | |
|     if (ctxt == NULL)
 | |
|         return;
 | |
|     ctxt->serror = serror;
 | |
|     ctxt->error = NULL;
 | |
|     ctxt->warning = NULL;
 | |
|     ctxt->userData = ctx;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronNewValidCtxt:
 | |
|  * @schema:  a precompiled XML Schematrons
 | |
|  * @options: a set of xmlSchematronValidOptions
 | |
|  *
 | |
|  * Create an XML Schematrons validation context based on the given schema.
 | |
|  *
 | |
|  * Returns the validation context or NULL in case of error
 | |
|  */
 | |
| xmlSchematronValidCtxtPtr
 | |
| xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
 | |
| {
 | |
|     int i;
 | |
|     xmlSchematronValidCtxtPtr ret;
 | |
| 
 | |
|     ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
 | |
|     if (ret == NULL) {
 | |
|         xmlSchematronVErrMemory(NULL, "allocating validation context",
 | |
|                                 NULL);
 | |
|         return (NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlSchematronValidCtxt));
 | |
|     ret->type = XML_STRON_CTXT_VALIDATOR;
 | |
|     ret->schema = schema;
 | |
|     ret->xctxt = xmlXPathNewContext(NULL);
 | |
|     ret->flags = options;
 | |
|     if (ret->xctxt == NULL) {
 | |
|         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
 | |
|                                 NULL);
 | |
| 	xmlSchematronFreeValidCtxt(ret);
 | |
|         return (NULL);
 | |
|     }
 | |
|     for (i = 0;i < schema->nbNamespaces;i++) {
 | |
|         if ((schema->namespaces[2 * i] == NULL) ||
 | |
|             (schema->namespaces[2 * i + 1] == NULL))
 | |
| 	    break;
 | |
| 	xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
 | |
| 	                   schema->namespaces[2 * i]);
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronFreeValidCtxt:
 | |
|  * @ctxt:  the schema validation context
 | |
|  *
 | |
|  * Free the resources associated to the schema validation context
 | |
|  */
 | |
| void
 | |
| xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
 | |
| {
 | |
|     if (ctxt == NULL)
 | |
|         return;
 | |
|     if (ctxt->xctxt != NULL)
 | |
|         xmlXPathFreeContext(ctxt->xctxt);
 | |
|     if (ctxt->dict != NULL)
 | |
|         xmlDictFree(ctxt->dict);
 | |
|     xmlFree(ctxt);
 | |
| }
 | |
| 
 | |
| static xmlNodePtr
 | |
| xmlSchematronNextNode(xmlNodePtr cur) {
 | |
|     if (cur->children != NULL) {
 | |
| 	/*
 | |
| 	 * Do not descend on entities declarations
 | |
| 	 */
 | |
| 	if (cur->children->type != XML_ENTITY_DECL) {
 | |
| 	    cur = cur->children;
 | |
| 	    /*
 | |
| 	     * Skip DTDs
 | |
| 	     */
 | |
| 	    if (cur->type != XML_DTD_NODE)
 | |
| 		return(cur);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     while (cur->next != NULL) {
 | |
| 	cur = cur->next;
 | |
| 	if ((cur->type != XML_ENTITY_DECL) &&
 | |
| 	    (cur->type != XML_DTD_NODE))
 | |
| 	    return(cur);
 | |
|     }
 | |
| 
 | |
|     do {
 | |
| 	cur = cur->parent;
 | |
| 	if (cur == NULL) break;
 | |
| 	if (cur->type == XML_DOCUMENT_NODE) return(NULL);
 | |
| 	if (cur->next != NULL) {
 | |
| 	    cur = cur->next;
 | |
| 	    return(cur);
 | |
| 	}
 | |
|     } while (cur != NULL);
 | |
|     return(cur);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronRunTest:
 | |
|  * @ctxt:  the schema validation context
 | |
|  * @test:  the current test
 | |
|  * @instance:  the document instace tree
 | |
|  * @cur:  the current node in the instance
 | |
|  *
 | |
|  * Validate a rule against a tree instance at a given position
 | |
|  *
 | |
|  * Returns 1 in case of success, 0 if error and -1 in case of internal error
 | |
|  */
 | |
| static int
 | |
| xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
 | |
|      xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
 | |
| {
 | |
|     xmlXPathObjectPtr ret;
 | |
|     int failed;
 | |
| 
 | |
|     failed = 0;
 | |
|     ctxt->xctxt->doc = instance;
 | |
|     ctxt->xctxt->node = cur;
 | |
|     ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
 | |
|     if (ret == NULL) {
 | |
| 	failed = 1;
 | |
|     } else {
 | |
|         switch (ret->type) {
 | |
| 	    case XPATH_XSLT_TREE:
 | |
| 	    case XPATH_NODESET:
 | |
| 		if ((ret->nodesetval == NULL) ||
 | |
| 		    (ret->nodesetval->nodeNr == 0))
 | |
| 		    failed = 1;
 | |
| 		break;
 | |
| 	    case XPATH_BOOLEAN:
 | |
| 		failed = !ret->boolval;
 | |
| 		break;
 | |
| 	    case XPATH_NUMBER:
 | |
| 		if ((xmlXPathIsNaN(ret->floatval)) ||
 | |
| 		    (ret->floatval == 0.0))
 | |
| 		    failed = 1;
 | |
| 		break;
 | |
| 	    case XPATH_STRING:
 | |
| 		if ((ret->stringval == NULL) ||
 | |
| 		    (ret->stringval[0] == 0))
 | |
| 		    failed = 1;
 | |
| 		break;
 | |
| 	    case XPATH_UNDEFINED:
 | |
| 	    case XPATH_POINT:
 | |
| 	    case XPATH_RANGE:
 | |
| 	    case XPATH_LOCATIONSET:
 | |
| 	    case XPATH_USERS:
 | |
| 		failed = 1;
 | |
| 		break;
 | |
| 	}
 | |
| 	xmlXPathFreeObject(ret);
 | |
|     }
 | |
|     if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
 | |
|         ctxt->nberrors++;
 | |
|     else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
 | |
|         ctxt->nberrors++;
 | |
| 
 | |
|     xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
 | |
| 
 | |
|     return(!failed);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlSchematronValidateDoc:
 | |
|  * @ctxt:  the schema validation context
 | |
|  * @instance:  the document instace tree
 | |
|  *
 | |
|  * Validate a tree instance against the schematron
 | |
|  *
 | |
|  * Returns 0 in case of success, -1 in case of internal error
 | |
|  *         and an error count otherwise.
 | |
|  */
 | |
| int
 | |
| xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
 | |
| {
 | |
|     xmlNodePtr cur, root;
 | |
|     xmlSchematronPatternPtr pattern;
 | |
|     xmlSchematronRulePtr rule;
 | |
|     xmlSchematronTestPtr test;
 | |
| 
 | |
|     if ((ctxt == NULL) || (ctxt->schema == NULL) ||
 | |
|         (ctxt->schema->rules == NULL) || (instance == NULL))
 | |
|         return(-1);
 | |
|     ctxt->nberrors = 0;
 | |
|     root = xmlDocGetRootElement(instance);
 | |
|     if (root == NULL) {
 | |
|         TODO
 | |
| 	ctxt->nberrors++;
 | |
| 	return(1);
 | |
|     }
 | |
|     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
 | |
|         (ctxt->flags == 0)) {
 | |
| 	/*
 | |
| 	 * we are just trying to assert the validity of the document,
 | |
| 	 * speed primes over the output, run in a single pass
 | |
| 	 */
 | |
| 	cur = root;
 | |
| 	while (cur != NULL) {
 | |
| 	    rule = ctxt->schema->rules;
 | |
| 	    while (rule != NULL) {
 | |
| 		if (xmlPatternMatch(rule->pattern, cur) == 1) {
 | |
| 		    test = rule->tests;
 | |
| 		    while (test != NULL) {
 | |
| 			xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
 | |
| 			test = test->next;
 | |
| 		    }
 | |
| 		}
 | |
| 		rule = rule->next;
 | |
| 	    }
 | |
| 
 | |
| 	    cur = xmlSchematronNextNode(cur);
 | |
| 	}
 | |
|     } else {
 | |
|         /*
 | |
| 	 * Process all contexts one at a time
 | |
| 	 */
 | |
| 	pattern = ctxt->schema->patterns;
 | |
| 
 | |
| 	while (pattern != NULL) {
 | |
| 	    xmlSchematronReportPattern(ctxt, pattern);
 | |
| 
 | |
| 	    /*
 | |
| 	     * TODO convert the pattern rule to a direct XPath and
 | |
| 	     * compute directly instead of using the pattern matching
 | |
| 	     * over the full document...
 | |
| 	     * Check the exact semantic
 | |
| 	     */
 | |
| 	    cur = root;
 | |
| 	    while (cur != NULL) {
 | |
| 		rule = pattern->rules;
 | |
| 		while (rule != NULL) {
 | |
| 		    if (xmlPatternMatch(rule->pattern, cur) == 1) {
 | |
| 			test = rule->tests;
 | |
| 			while (test != NULL) {
 | |
| 			    xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
 | |
| 			    test = test->next;
 | |
| 			}
 | |
| 		    }
 | |
| 		    rule = rule->patnext;
 | |
| 		}
 | |
| 
 | |
| 		cur = xmlSchematronNextNode(cur);
 | |
| 	    }
 | |
| 	    pattern = pattern->next;
 | |
| 	}
 | |
|     }
 | |
|     return(ctxt->nberrors);
 | |
| }
 | |
| 
 | |
| #ifdef STANDALONE
 | |
| int
 | |
| main(void)
 | |
| {
 | |
|     int ret;
 | |
|     xmlDocPtr instance;
 | |
|     xmlSchematronParserCtxtPtr pctxt;
 | |
|     xmlSchematronValidCtxtPtr vctxt;
 | |
|     xmlSchematronPtr schema = NULL;
 | |
| 
 | |
|     pctxt = xmlSchematronNewParserCtxt("tst.sct");
 | |
|     if (pctxt == NULL) {
 | |
|         fprintf(stderr, "failed to build schematron parser\n");
 | |
|     } else {
 | |
|         schema = xmlSchematronParse(pctxt);
 | |
| 	if (schema == NULL) {
 | |
| 	    fprintf(stderr, "failed to compile schematron\n");
 | |
| 	}
 | |
| 	xmlSchematronFreeParserCtxt(pctxt);
 | |
|     }
 | |
|     instance = xmlReadFile("tst.sct", NULL,
 | |
|                            XML_PARSE_NOENT | XML_PARSE_NOCDATA);
 | |
|     if (instance == NULL) {
 | |
| 	fprintf(stderr, "failed to parse instance\n");
 | |
|     }
 | |
|     if ((schema != NULL) && (instance != NULL)) {
 | |
|         vctxt = xmlSchematronNewValidCtxt(schema);
 | |
| 	if (vctxt == NULL) {
 | |
| 	    fprintf(stderr, "failed to build schematron validator\n");
 | |
| 	} else {
 | |
| 	    ret = xmlSchematronValidateDoc(vctxt, instance);
 | |
| 	    xmlSchematronFreeValidCtxt(vctxt);
 | |
| 	}
 | |
|     }
 | |
|     xmlSchematronFree(schema);
 | |
|     xmlFreeDoc(instance);
 | |
| 
 | |
|     xmlCleanupParser();
 | |
|     xmlMemoryDump();
 | |
| 
 | |
|     return (0);
 | |
| }
 | |
| #endif
 | |
| #define bottom_schematron
 | |
| #include "elfgcchack.h"
 | |
| #endif /* LIBXML_SCHEMATRON_ENABLED */
 |