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