~win-cross-dev/win-cross/gettext

« back to all changes in this revision

Viewing changes to gettext-tools/gnulib-lib/libxml/schematron.c

  • Committer: Nathan Osman
  • Date: 2012-08-11 05:06:52 UTC
  • Revision ID: admin@quickmediasolutions.com-20120811050652-ochkxjtonbw6kkve
Initial commit.

Show diffs side-by-side

added added

removed removed

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