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