File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / schematron.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:37:58 2012 UTC (12 years, 4 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_8_0p0, v2_8_0, v2_7_8, HEAD
libxml2

    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>