schematron.c 61 KB

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