tDOM

Check-in [72ce7820c1]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merged from schema.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | localkey
Files: files | file ages | folders
SHA3-256: 72ce7820c1aafc8a7c64fcddc2a5b43492166cbf56c46239383d6b1f772a2222
User & Date: rolf 2019-05-21 22:37:42
Context
2019-05-21
23:56
Save work. check-in: ddad141351 user: rolf tags: localkey
22:37
Merged from schema. check-in: 72ce7820c1 user: rolf tags: localkey
22:30
Added dom tree validation specific unique key contraints along the lines of xsd unqiue with selector and list of fields but with no restrictions on the XPath expressions (other then resulting a node set in case of selector and a result set with one node in case of field XPath expression). check-in: ba6bd2bb7c user: rolf tags: schema
2019-05-18
00:41
Save work. check-in: 4592100a23 user: rolf tags: localkey
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/dom.c.

45
46
47
48
49
50
51

52
53
54
55
56
57
58
/*---------------------------------------------------------------------------
|   Includes
|
\--------------------------------------------------------------------------*/
#include <tcl.h>
#include <dom.h>
#include <domxpath.h>

#include <tclexpat.h>


/* #define DEBUG */
/*----------------------------------------------------------------------------
|   Debug Macros
|






>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*---------------------------------------------------------------------------
|   Includes
|
\--------------------------------------------------------------------------*/
#include <tcl.h>
#include <dom.h>
#include <domxpath.h>
#include <schema.h>
#include <tclexpat.h>


/* #define DEBUG */
/*----------------------------------------------------------------------------
|   Debug Macros
|

Changes to generic/dom.h.

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
699
700
701
702
703
704
705






706
707
708
709
710
711
712
#include <tcl.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <expat.h>
#include <domalloc.h>
#include <schema.h>

/*
 * tDOM provides it's own memory allocator which is optimized for
 * low heap usage. It uses the native Tcl allocator underneath,
 * though, but it is not very MT-friendly. Therefore, you might
 * use the (normal) Tcl allocator with USE_NORMAL_ALLOCATOR
 * defined during compile time. Actually, the symbols name is 
................................................................................

/*--------------------------------------------------------------------------
|   domAddCallback
|
\-------------------------------------------------------------------------*/
typedef int  (*domAddCallback)  (domNode * node, void * clientData);
typedef void (*domFreeCallback) (domNode * node, void * clientData);







/*--------------------------------------------------------------------------
|   Function prototypes
|
\-------------------------------------------------------------------------*/
const char *   domException2String (domException exception);







<







 







>
>
>
>
>
>







38
39
40
41
42
43
44

45
46
47
48
49
50
51
...
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
#include <tcl.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <expat.h>
#include <domalloc.h>


/*
 * tDOM provides it's own memory allocator which is optimized for
 * low heap usage. It uses the native Tcl allocator underneath,
 * though, but it is not very MT-friendly. Therefore, you might
 * use the (normal) Tcl allocator with USE_NORMAL_ALLOCATOR
 * defined during compile time. Actually, the symbols name is 
................................................................................

/*--------------------------------------------------------------------------
|   domAddCallback
|
\-------------------------------------------------------------------------*/
typedef int  (*domAddCallback)  (domNode * node, void * clientData);
typedef void (*domFreeCallback) (domNode * node, void * clientData);

/*--------------------------------------------------------------------------
|   Forward declaration
|
\-------------------------------------------------------------------------*/
typedef struct SchemaData_ SchemaData;

/*--------------------------------------------------------------------------
|   Function prototypes
|
\-------------------------------------------------------------------------*/
const char *   domException2String (domException exception);

Changes to generic/domxpath.c.

260
261
262
263
264
265
266






















267
268
269
270
271
272
273
....
5222
5223
5224
5225
5226
5227
5228





























































5229
5230
5231
5232
5233
5234
5235
        rs->nr_nodes  = 0;
    } else 
    if (rs->type == StringResult) {
        if (rs->string) FREE((char*)rs->string);
    }
    rs->type = EmptyResult;
}























void rsPrint ( xpathResultSet *rs ) {
    int i = 0,l;  char tmp[80];
    switch (rs->type) {
        case EmptyResult:
             fprintf(stderr, "empty result \n");
             break;
................................................................................
    CHECK_RC;

    DBG(rsPrint( result );)
    return 0;

} /* xpathEval */






























































/*----------------------------------------------------------------------------
|   xpathMatches
|
\---------------------------------------------------------------------------*/
int xpathMatches (
    ast                 step,
    domNode           * exprContext,






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
....
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
        rs->nr_nodes  = 0;
    } else 
    if (rs->type == StringResult) {
        if (rs->string) FREE((char*)rs->string);
    }
    rs->type = EmptyResult;
}

void
xpathRSReset (
    xpathResultSet *rs,
    domNode *node
    ) 
{
    if (rs->type == StringResult) FREE(rs->string);
    if (node) {
        if (!rs->nodes) {
            rs->nodes     = (domNode**)MALLOC( INITIAL_SIZE*sizeof(domNode*));
            rs->allocated = INITIAL_SIZE;
        }
        rs->nodes[0] = node;
        rs->nr_nodes = 1;
        rs->type = xNodeSetResult;
    } else {
        rs->nr_nodes = 0;
        if (rs->nodes) rs->type = xNodeSetResult;
        else rs->type = EmptyResult;
    }
}

void rsPrint ( xpathResultSet *rs ) {
    int i = 0,l;  char tmp[80];
    switch (rs->type) {
        case EmptyResult:
             fprintf(stderr, "empty result \n");
             break;
................................................................................
    CHECK_RC;

    DBG(rsPrint( result );)
    return 0;

} /* xpathEval */


int
xpathEvalAst (
    ast             t,
    xpathResultSet *nodeList,
    domNode        *node,
    xpathResultSet *rs,
    char          **errMsg
    )
{
    int i, rc, first = 1, docOrder = 1;
    xpathResultSet savedContext;
    savedContext = *nodeList;

    while (t) {
        DBG (fprintf (stderr, "xpathEvalAst: eval step '%s'\n", 
                      astType2str[t->type]);)
        if (t->type == Pred) {
            *errMsg = "Pred step not expected now!";
            return XPATH_EVAL_ERR;
        }
        if (first) {
            rc = xpathEvalStepAndPredicates (t, nodeList, node,
                                             node, 0, &docOrder,
                                             NULL, rs, errMsg);
            CHECK_RC;
            first = 0;
        } else {
            DBG( fprintf(stderr, "doing location step nodeList->nr_nodes=%d \n",
                                 nodeList->nr_nodes);
            )
            if (rs->type != xNodeSetResult) {
                *nodeList = savedContext;
                return 0;
            }

            *nodeList = *rs;
            xpathRSReset (rs, NULL);
            for (i=0; i < nodeList->nr_nodes; i++) {
                rc = xpathEvalStepAndPredicates (t, nodeList,
                                                 nodeList->nodes[i],
                                                 node, i, &docOrder, NULL,
                                                 rs, errMsg);
                if (rc) {
                    *nodeList = savedContext;
                    return rc;
                }
            }
        }
        DBG( fprintf(stderr, "result after location step: \n"); )
        DBG( rsPrint( result); )

        t = t->next;
        /* skip the already processed Predicate parts */
        while (t && t->type == Pred) t = t->next;
        docOrder = 1;
    }
    *nodeList = savedContext;
    return 0;
}

/*----------------------------------------------------------------------------
|   xpathMatches
|
\---------------------------------------------------------------------------*/
int xpathMatches (
    ast                 step,
    domNode           * exprContext,

Changes to generic/domxpath.h.

171
172
173
174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194
195
196
197
198
                     ast *t, char **errMsg);
void   xpathFreeAst (ast t);
double xpathGetPrio (ast t);
int    xpathEval    (domNode *node, domNode *exprContext, char *xpath, 
                     char **prefixMappings, xpathCBs *cbs,
                     xpathParseVarCB *parseVarCB, Tcl_HashTable *catch, 
                     char **errMsg, xpathResultSet *rs);


int    xpathMatches (ast steps, domNode * exprContext, domNode *nodeToMatch,
                     xpathCBs *cbs, char **errMsg 
                    );
                     
int xpathEvalSteps (ast steps, xpathResultSet *nodeList,
                    domNode *currentNode, domNode *exprContext, int currentPos,
                    int *docOrder,
                    xpathCBs *cbs,
                    xpathResultSet *result, char **errMsg);
                    
#define xpathRSInit(x) (x)->type = EmptyResult; \
                       (x)->intvalue = 0; \
                       (x)->nr_nodes = 0;
void   xpathRSFree (xpathResultSet *rs );


int    xpathFuncBoolean  (xpathResultSet *rs);
double xpathFuncNumber   (xpathResultSet *rs, int *NaN);
char * xpathFuncString   (xpathResultSet *rs); 
char * xpathFuncStringForNode (domNode *node);
int    xpathRound        (double r);







>
>













|
>







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
                     ast *t, char **errMsg);
void   xpathFreeAst (ast t);
double xpathGetPrio (ast t);
int    xpathEval    (domNode *node, domNode *exprContext, char *xpath, 
                     char **prefixMappings, xpathCBs *cbs,
                     xpathParseVarCB *parseVarCB, Tcl_HashTable *catch, 
                     char **errMsg, xpathResultSet *rs);
int    xpathEvalAst (ast t, xpathResultSet *nodeList, domNode *node,
                     xpathResultSet *rs, char **errMsg);
int    xpathMatches (ast steps, domNode * exprContext, domNode *nodeToMatch,
                     xpathCBs *cbs, char **errMsg 
                    );
                     
int xpathEvalSteps (ast steps, xpathResultSet *nodeList,
                    domNode *currentNode, domNode *exprContext, int currentPos,
                    int *docOrder,
                    xpathCBs *cbs,
                    xpathResultSet *result, char **errMsg);
                    
#define xpathRSInit(x) (x)->type = EmptyResult; \
                       (x)->intvalue = 0; \
                       (x)->nr_nodes = 0;
void   xpathRSFree (xpathResultSet *rs);
void   xpathRSReset (xpathResultSet *rs, domNode *ode);

int    xpathFuncBoolean  (xpathResultSet *rs);
double xpathFuncNumber   (xpathResultSet *rs, int *NaN);
char * xpathFuncString   (xpathResultSet *rs); 
char * xpathFuncStringForNode (domNode *node);
int    xpathRound        (double r);

Changes to generic/schema.c.

82
83
84
85
86
87
88







89
90
91
92
93
94
95
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
418
419
420
421
422
423
424





















425
426
427
428
429
430
431
...
454
455
456
457
458
459
460

461
462
463
464
465
466
467
....
2182
2183
2184
2185
2186
2187
2188





























































































































































































































2189
2190
2191
2192
2193
2194
2195
....
2231
2232
2233
2234
2235
2236
2237





2238
2239
2240
2241
2242
2243
2244
....
3802
3803
3804
3805
3806
3807
3808



















































































3809
3810
3811
3812
3813
3814
3815
....
5108
5109
5110
5111
5112
5113
5114



5115
5116
5117
5118
5119
5120
5121
5122
    Tcl_Interp    *interp;
    XML_Parser     parser;
    Tcl_DString   *cdata;
    int            onlyWhiteSpace;
    char          *uri;
    int            maxUriLen;
} ValidateMethodData;








/*----------------------------------------------------------------------------
|   Macros
|
\---------------------------------------------------------------------------*/
#define TMALLOC(t) (t*)MALLOC(sizeof(t))

................................................................................
#define SetResult3(str1,str2,str3) Tcl_ResetResult(interp);     \
                     Tcl_AppendResult(interp, (str1), (str2), (str3), NULL)
#define SetIntResult(i) Tcl_ResetResult(interp);                        \
                     Tcl_SetIntObj(Tcl_GetObjResult(interp), (i))
#define SetBooleanResult(i) Tcl_ResetResult(interp); \
                     Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (i))

#define SPACE(c) ((c) == ' ' || (c) == '\n' || (c )== '\t' || (c) == '\r')
    
#define checkNrArgs(l,h,err) if (objc < l || objc > h) {      \
        SetResult (err);                                      \
        return TCL_ERROR;                                     \
    }

#if defined(DEBUG) || defined(DDEBUG)
................................................................................
        }
        FREE (kc->fields);
        FREE (kc);
        kc = knext;
    }
}






















static void freeSchemaCP (
    SchemaCP *pattern
    )
{
    int i;
    SchemaConstraint *sc;

................................................................................
        if (pattern->attrs) {
            for (i = 0; i < pattern->numAttr; i++) {
                FREE (pattern->attrs[i]);
            }
            FREE (pattern->attrs);
        }
        freeKeyConstraints (pattern->localkeys);

        break;
    }
    FREE (pattern);
}

static SchemaData*
initSchemaData (
................................................................................
    }
    XML_ParserFree (parser);
    Tcl_DStringFree (&cdata);
    FREE (vdata.uri);
    while (sdata->stack) popStack (sdata);
    return result;
}






























































































































































































































static int
validateDOM (
    Tcl_Interp *interp,
    SchemaData *sdata,
    domNode    *node
    )
................................................................................
            /* probeDomAttributes fills interp result with a msg which
             * required attributes are missing. */
            probeDomAttributes (interp, sdata, NULL);
            return TCL_ERROR;
        }
    }






    node = node->firstChild;
    while (node) {
        switch (node->nodeType) {
        case ELEMENT_NODE:
            if (Tcl_DStringLength (sdata->cdata)) {
                if (probeText (interp, sdata,
                               Tcl_DStringValue (sdata->cdata)) != TCL_OK)
................................................................................
        while (kc1->next) kc1 = kc1->next;
        kc1->next = kc;
    } else {
        sdata->cp->localkeys = kc;
    }
    return TCL_OK;
}




















































































static int
integerImpl (
    Tcl_Interp *interp,
    void *constraintData,
    char *text
    )
................................................................................
    /* The 'virtual' "tcl" definition command */
    Tcl_CreateObjCommand (interp, "tdom::schema::tcl",
                          VirtualPatternObjCmd, NULL, NULL);

    /* Identity definition commands */
    Tcl_CreateObjCommand (interp,"tdom::schema::unique",
                          uniquePatternCmd, NULL, NULL);




    /* The text constraint commands */
    Tcl_CreateObjCommand (interp,"tdom::schema::text::integer",
                          integerTCObjCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::text::tcl",
                          tclTCObjCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::text::fixed",
                          fixedTCObjCmd, NULL, NULL);






>
>
>
>
>
>
>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
...
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
....
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
....
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
....
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
....
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
    Tcl_Interp    *interp;
    XML_Parser     parser;
    Tcl_DString   *cdata;
    int            onlyWhiteSpace;
    char          *uri;
    int            maxUriLen;
} ValidateMethodData;

/*----------------------------------------------------------------------------
|   domKeyConstraint related flage
|
\---------------------------------------------------------------------------*/

#define DKC_FLAG_IGNORE_EMPTY_FIELD_SET 1

/*----------------------------------------------------------------------------
|   Macros
|
\---------------------------------------------------------------------------*/
#define TMALLOC(t) (t*)MALLOC(sizeof(t))

................................................................................
#define SetResult3(str1,str2,str3) Tcl_ResetResult(interp);     \
                     Tcl_AppendResult(interp, (str1), (str2), (str3), NULL)
#define SetIntResult(i) Tcl_ResetResult(interp);                        \
                     Tcl_SetIntObj(Tcl_GetObjResult(interp), (i))
#define SetBooleanResult(i) Tcl_ResetResult(interp); \
                     Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (i))

#define SPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t' || (c) == '\r')
    
#define checkNrArgs(l,h,err) if (objc < l || objc > h) {      \
        SetResult (err);                                      \
        return TCL_ERROR;                                     \
    }

#if defined(DEBUG) || defined(DDEBUG)
................................................................................
        }
        FREE (kc->fields);
        FREE (kc);
        kc = knext;
    }
}


static void freedomKeyConstraints (
    domKeyConstraint *kc
    )
{
    domKeyConstraint *knext;
    int i;

    while (kc) {
        knext = kc->next;
        if (kc->name) FREE (kc->name);
        xpathFreeAst (kc->selector);
        for (i = 0; i < kc->nrFields; i++) {
            xpathFreeAst (kc->fields[i]);
        }
        FREE (kc->fields);
        FREE (kc);
        kc = knext;
    }
}

static void freeSchemaCP (
    SchemaCP *pattern
    )
{
    int i;
    SchemaConstraint *sc;

................................................................................
        if (pattern->attrs) {
            for (i = 0; i < pattern->numAttr; i++) {
                FREE (pattern->attrs[i]);
            }
            FREE (pattern->attrs);
        }
        freeKeyConstraints (pattern->localkeys);
        freedomKeyConstraints (pattern->domKeys);
        break;
    }
    FREE (pattern);
}

static SchemaData*
initSchemaData (
................................................................................
    }
    XML_ParserFree (parser);
    Tcl_DStringFree (&cdata);
    FREE (vdata.uri);
    while (sdata->stack) popStack (sdata);
    return result;
}

static void
schemaxpathRSFree (
    xpathResultSet *rs
    )
{
    if (rs->type == StringResult) FREE (rs->string);
    FREE (rs->nodes);
}

static int
checkdomKeyConstraints (
    Tcl_Interp *interp,
    SchemaData *sdata,
    domNode    *node
    )
{
    xpathResultSet nodeList, rs, frs;
    domKeyConstraint *kc;
    domNode *n;
    domAttrNode *attr;
    int rc, i, j, hnew, len, skip, first;
    char *errMsg = NULL, *keystr;
    Tcl_HashTable htable;
    Tcl_DString dStr;

    kc = sdata->stack->pattern->domKeys;
    memset (&nodeList, 0, sizeof (xpathResultSet));
    nodeList.type = EmptyResult;
    memset (&rs, 0, sizeof (xpathResultSet));
    rs.type = EmptyResult;
    memset (&frs, 0, sizeof (xpathResultSet));
    frs.type = EmptyResult;
    Tcl_DStringInit (&dStr);
    while (kc) {
        xpathRSReset (&rs, NULL);
        xpathRSReset (&nodeList, node);
        Tcl_InitHashTable (&htable, TCL_STRING_KEYS);
        rc = xpathEvalAst (kc->selector, &nodeList, node, &rs, &errMsg);
        if (rc) {
            if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                goto nextConstraint;
            }
            goto errorCleanup;
        }
        if (rs.type == EmptyResult) goto nextConstraint;
        if (rs.type != xNodeSetResult) {
            if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                goto nextConstraint;
            }
            SetResult ("INVALID_DOM_KEYCONSTRAINT");
            goto errorCleanup;
        }
        for (i = 0; i < rs.nr_nodes; i++) {
            n = rs.nodes[i];
            if (n->nodeType != ELEMENT_NODE) {
                if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                    break;
                }
                SetResult ("INVALID_DOM_KEYCONSTRAINT");
                goto errorCleanup;
            }
            xpathRSReset (&nodeList, n);
            if (kc->nrFields == 1) {
                xpathRSReset (&frs, NULL);
                rc = xpathEvalAst (kc->fields[0], &nodeList, n, &frs,
                                   &errMsg);
                if (rc) {
                    if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                        break;
                    }
                    SetResult ("INVALID_DOM_KEYCONSTRAINT");
                    goto errorCleanup;
                }
                if (frs.type != xNodeSetResult
                    && frs.type != EmptyResult) {
                    if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                        break;
                    }
                    SetResult ("INVALID_DOM_KEYCONSTRAINT");
                    goto errorCleanup;
                }
                if (frs.type == EmptyResult || frs.nr_nodes == 0) {
                    if (kc->flags & DKC_FLAG_IGNORE_EMPTY_FIELD_SET) {
                        continue;
                    }
                    Tcl_CreateHashEntry (&htable, "", &hnew);
                    if (!hnew) {
                        if (recover (interp, sdata, S("DOM_KEYCONSTRAINT"))) {
                            break;
                        }
                        SetResult ("DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                    continue;
                }
                if (frs.nr_nodes != 1) {
                    if (recover (interp, sdata, S("DOM_KEYCONSTRAINT"))) {
                        break;
                    }
                    SetResult ("DOM_KEYCONSTRAINT");
                    goto errorCleanup;
                }
                if (frs.nodes[0]->nodeType != ELEMENT_NODE
                    && frs.nodes[0]->nodeType != ATTRIBUTE_NODE) {
                    if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                        break;
                    }
                    SetResult ("INVALID_DOM_KEYCONSTRAINT");
                    goto errorCleanup;
                }
                if (frs.nodes[0]->nodeType == ATTRIBUTE_NODE) {
                    attr = (domAttrNode *) frs.nodes[0];
                    Tcl_CreateHashEntry (&htable, attr->nodeValue, &hnew);
                    if (!hnew) {
                        if (recover (interp, sdata, S("DOM_KEYCONSTRAINT"))) {
                            break;
                        }
                        SetResult ("DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                } else {
                    keystr = xpathGetStringValue (frs.nodes[0], &len);
                    Tcl_CreateHashEntry (&htable, attr->nodeValue, &hnew);
                    FREE(keystr);
                    if (!hnew) {
                        if (recover (interp, sdata, S("DOM_KEYCONSTRAINT"))) {
                            break;
                        }
                        SetResult ("DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                }
            } else {
                Tcl_DStringSetLength (&dStr, 0);
                skip = 0;
                first = 1;
                for (j = 0; j < kc->nrFields; j++) {
                    xpathRSReset (&frs, NULL);
                    rc = xpathEvalAst (kc->fields[j], &nodeList, n, &frs,
                                       &errMsg);
                    if (rc) {
                        if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                            skip = 1;
                            break;
                        }
                        SetResult ("INVALID_DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                    if (frs.type != xNodeSetResult
                        && frs.type != EmptyResult) {
                        if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                            skip = 1;
                            break;
                        }
                        SetResult ("INVALID_DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                    if (frs.type == EmptyResult || frs.nr_nodes == 0) {
                        if (kc->flags & DKC_FLAG_IGNORE_EMPTY_FIELD_SET) {
                            continue;
                        }
                        if (first) first = 0;
                        else Tcl_DStringAppend (&dStr, ":", 1);
                        continue;
                    }
                    if (frs.nr_nodes != 1) {
                        if (recover (interp, sdata, S("DOM_KEYCONSTRAINT"))) {
                            skip = 1;
                            break;
                        }
                        SetResult ("DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                    if (frs.nodes[0]->nodeType != ELEMENT_NODE
                        && frs.nodes[0]->nodeType != ATTRIBUTE_NODE) {
                        if (recover (interp, sdata, S("INVALID_DOM_KEYCONSTRAINT"))) {
                            skip = 1;
                            break;
                        }
                        SetResult ("INVALID_DOM_KEYCONSTRAINT");
                        goto errorCleanup;
                    }
                    if (first) first = 0;
                    else Tcl_DStringAppend (&dStr, ":", 1);
                    if (frs.nodes[0]->nodeType == ATTRIBUTE_NODE) {
                        attr = (domAttrNode *) frs.nodes[0];
                        Tcl_DStringAppend (&dStr, attr->nodeValue,
                                           attr->valueLength);
                    } else {
                        keystr = xpathGetStringValue (frs.nodes[0], &len);
                        Tcl_DStringAppend (&dStr, keystr, len);
                    }
                }
                if (skip) break;
                Tcl_CreateHashEntry (&htable, Tcl_DStringValue (&dStr), &hnew);
                if (!hnew) {
                    if (recover (interp, sdata, S("DOM_KEYCONSTRAINT"))) {
                        break;
                    }
                    SetResult ("DOM_KEYCONSTRAINT");
                    goto errorCleanup;
                }
            }
        }
    nextConstraint:
        Tcl_DeleteHashTable (&htable);
        kc = kc->next;
    }
    schemaxpathRSFree (&frs);
    schemaxpathRSFree (&rs);
    schemaxpathRSFree (&nodeList);
    return TCL_OK;

errorCleanup:
    Tcl_DeleteHashTable (&htable);
    schemaxpathRSFree (&frs);
    schemaxpathRSFree (&rs);
    schemaxpathRSFree (&nodeList);
    return TCL_ERROR;
}

static int
validateDOM (
    Tcl_Interp *interp,
    SchemaData *sdata,
    domNode    *node
    )
................................................................................
            /* probeDomAttributes fills interp result with a msg which
             * required attributes are missing. */
            probeDomAttributes (interp, sdata, NULL);
            return TCL_ERROR;
        }
    }

    if (sdata->stack->pattern->domKeys) {
        if (checkdomKeyConstraints (interp, sdata, node) != TCL_OK)
            return TCL_ERROR;
    }
    
    node = node->firstChild;
    while (node) {
        switch (node->nodeType) {
        case ELEMENT_NODE:
            if (Tcl_DStringLength (sdata->cdata)) {
                if (probeText (interp, sdata,
                               Tcl_DStringValue (sdata->cdata)) != TCL_OK)
................................................................................
        while (kc1->next) kc1 = kc1->next;
        kc1->next = kc;
    } else {
        sdata->cp->localkeys = kc;
    }
    return TCL_OK;
}

static int
domuniquePatternCmd (
    ClientData clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[]
    )
{
    SchemaData *sdata = GETASI;
    ast t;
    char *errMsg = NULL;
    domKeyConstraint *kc;
    int i, nrFields, flags = 0, nrFlags;
    Tcl_Obj *elm;

    CHECK_SI
    CHECK_TOPLEVEL
    checkNrArgs (3, 5, "Expected: <selector> <fieldlist> ?<name>? ?flags?");
    if (sdata->cp->type != SCHEMA_CTYPE_NAME) {
        SetResult ("The domunique schema definition command is only "
                   "allowed as direct child of an element.");
    }
    if (Tcl_ListObjLength (interp, objv[2], &nrFields) != TCL_OK) {
        SetResult ("The <fieldlist> argument must be a valid tcl list");
        return TCL_ERROR;
    }
    if (nrFields == 0) {
        SetResult ("Non empty fieldlist arugment expected.");
        xpathFreeAst (t);
        return TCL_ERROR;
    }
    if (objc == 5) {
        if (Tcl_ListObjLength (interp, objv[4], &nrFlags) != TCL_OK) {
            SetResult ("The <flags> argument must be a valid tcl list");
            return TCL_ERROR;
        }
        for (i = 0; i < nrFlags; i++) {
            Tcl_ListObjIndex (interp, objv[4], i, &elm);
            if (strcmp ("IGNORE_EMPTY_FIELD_SET", Tcl_GetString (elm)) == 0) {
                flags |= DKC_FLAG_IGNORE_EMPTY_FIELD_SET;
                continue;
            }
            SetResult3 ("Unknown flag '", Tcl_GetString (elm), "'");
            return TCL_ERROR;
        }
    }
    
    if (xpathParse (Tcl_GetString (objv[1]), NULL, XPATH_EXPR,
                    sdata->prefixns, NULL, &t, &errMsg) < 0) {
        SetResult3 ("Error in selector xpath: '", errMsg, "");
        FREE (errMsg);
        return TCL_ERROR;
    }

    
    kc = TMALLOC (domKeyConstraint);
    memset (kc, 0, sizeof (domKeyConstraint));
    kc->fields = MALLOC (sizeof (ast) * nrFields);
    memset (kc->fields, 0, sizeof (ast) * nrFields);
    kc->nrFields = nrFields;
    kc->selector = t;
    kc->flags = flags;
    
    for (i = 0; i < nrFields; i++) {
        Tcl_ListObjIndex (interp, objv[2], i, &elm);
        if (xpathParse (Tcl_GetString (elm), NULL, XPATH_EXPR,
                        sdata->prefixns, NULL, &t, &errMsg) < 0) {
            SetResult3 ("Error in field xpath: '", errMsg, "");
            FREE (errMsg);
            xpathFreeAst (t);
            freedomKeyConstraints (kc);
            return TCL_ERROR;
        }
        kc->fields[i] = t;
    }
    if (objc == 4) {
        kc->name = tdomstrdup (Tcl_GetString (objv[3]));
    }
    kc->next = sdata->cp->domKeys;
    sdata->cp->domKeys = kc;
    return TCL_OK;
}

static int
integerImpl (
    Tcl_Interp *interp,
    void *constraintData,
    char *text
    )
................................................................................
    /* The 'virtual' "tcl" definition command */
    Tcl_CreateObjCommand (interp, "tdom::schema::tcl",
                          VirtualPatternObjCmd, NULL, NULL);

    /* Identity definition commands */
    Tcl_CreateObjCommand (interp,"tdom::schema::unique",
                          uniquePatternCmd, NULL, NULL);
    /* XPath contraints for DOM validation */
    Tcl_CreateObjCommand (interp,"tdom::schema::domunique",
                          domuniquePatternCmd, NULL, NULL);
    
    /* The text constraint commands */
    Tcl_CreateObjCommand (interp,"tdom::schema::text::integer",
                          integerTCObjCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::text::tcl",
                          tclTCObjCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::text::fixed",
                          fixedTCObjCmd, NULL, NULL);

Changes to generic/schema.h.

20
21
22
23
24
25
26



27
28
29
30
31
32
33
..
67
68
69
70
71
72
73









74
75
76
77
78
79
80
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|   2018-2019
|
\---------------------------------------------------------------------------*/

#ifndef __SCHEMA_H__
#define __SCHEMA_H__




typedef enum {
  SCHEMA_CTYPE_ANY,
  SCHEMA_CTYPE_NAME,
  SCHEMA_CTYPE_CHOICE,
  SCHEMA_CTYPE_INTERLEAVE,
  SCHEMA_CTYPE_PATTERN,
  SCHEMA_CTYPE_TEXT,
................................................................................
/* The SchemaFlags flags */
#define FORWARD_PATTERN_DEF     1
#define PLACEHOLDER_PATTERN_DEF 2
#define AMBIGUOUS_PATTERN       4
#define LOCAL_DEFINED_ELEMENT   8
#define CONSTRAINT_TEXT_CHILD  16
#define MIXED_CONTENT          32 











typedef enum {
  SCHEMA_KEY_UNIQUE,
  SCHEMA_KEY_KEY,
  SCHEMA_KEY_KEYREF
} KeyType;
................................................................................
    struct SchemaCP **content;
    SchemaQuant      *quants;
    unsigned int      nc;
    SchemaAttr      **attrs;
    unsigned int      numAttr;
    unsigned int      numReqAttr;
    KeyConstraint    *localkeys;
    int               nrKeys;
} SchemaCP;

typedef struct SchemaValidationStack
{
    SchemaCP *pattern;
    struct SchemaValidationStack *next;
    struct SchemaValidationStack *down;
................................................................................

typedef struct 
{
    Tcl_HashTable ids;
    int unknownIDrefs;
} SchemaDocKey;
    
typedef struct 
{
    Tcl_Obj *self;
    char *start;
    char *startNamespace;
    Tcl_HashTable element;
    Tcl_HashTable namespace;
    Tcl_HashEntry *emptyNamespace;






>
>
>







 







>
>
>
>
>
>
>
>
>







 







|







 







|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|   2018-2019
|
\---------------------------------------------------------------------------*/

#ifndef __SCHEMA_H__
#define __SCHEMA_H__

#include <tcldom.h>
#include <domxpath.h>

typedef enum {
  SCHEMA_CTYPE_ANY,
  SCHEMA_CTYPE_NAME,
  SCHEMA_CTYPE_CHOICE,
  SCHEMA_CTYPE_INTERLEAVE,
  SCHEMA_CTYPE_PATTERN,
  SCHEMA_CTYPE_TEXT,
................................................................................
/* The SchemaFlags flags */
#define FORWARD_PATTERN_DEF     1
#define PLACEHOLDER_PATTERN_DEF 2
#define AMBIGUOUS_PATTERN       4
#define LOCAL_DEFINED_ELEMENT   8
#define CONSTRAINT_TEXT_CHILD  16
#define MIXED_CONTENT          32 

typedef struct domKeyConstraint {
    char  *name;
    ast    selector;
    ast   *fields;
    int    nrFields;
    int    flags;
    struct domKeyConstraint *next;
} domKeyConstraint;


typedef enum {
  SCHEMA_KEY_UNIQUE,
  SCHEMA_KEY_KEY,
  SCHEMA_KEY_KEYREF
} KeyType;
................................................................................
    struct SchemaCP **content;
    SchemaQuant      *quants;
    unsigned int      nc;
    SchemaAttr      **attrs;
    unsigned int      numAttr;
    unsigned int      numReqAttr;
    KeyConstraint    *localkeys;
    domKeyConstraint *domKeys;
} SchemaCP;

typedef struct SchemaValidationStack
{
    SchemaCP *pattern;
    struct SchemaValidationStack *next;
    struct SchemaValidationStack *down;
................................................................................

typedef struct 
{
    Tcl_HashTable ids;
    int unknownIDrefs;
} SchemaDocKey;
    
typedef struct SchemaData_
{
    Tcl_Obj *self;
    char *start;
    char *startNamespace;
    Tcl_HashTable element;
    Tcl_HashTable namespace;
    Tcl_HashEntry *emptyNamespace;

Changes to tests/schema.test.

4742
4743
4744
4745
4746
4747
4748
4749





4750



































































































































        {<doc><item lang="de"/><item lang="en"/><item lang="fr"/></doc>}
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {1}






}










































































































































>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
        {<doc><item lang="de"/><item lang="en"/><item lang="fr"/></doc>}
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {1}

test schema-20.1 {domunique} {
    set schema {
        prefixns {ns1 http://tdom.org/test}
        defelement doc {
            domunique ${::schema-20.1} @ref
        }
    }
    set result [list]
    foreach ::schema-20.1 {
        a
        ./../a
        /foo
        a/b
        {a | b}
        a|b
        (a|b)
        {a/b/c | b/c/d | c/d/e}
        .//a
        //a
        a/@ref
        a/b/c
        {a//b[1]/c}
        (.//b|a)/c
        ns1:a
        a/.//b/c
        {}
        {    }
        "
                "
    } {
        tdom::schema s
        lappend result [catch {s define $schema} errMsg]
        #puts $errMsg
        s delete
    }
    set result
} {0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1}

test schema-20.2 {domunique} {
    tdom::schema s
    s define {
        defelement doc {
            domunique item @ref
            element item * {
                attribute ref ?
            }
        }
    }
    set result [list]
    foreach xml {
        {<doc><item ref="1"/><item ref="foo"/></doc>}
        {<doc><item ref="1"/><item ref="1"/></doc>}
        {<doc><item/><item ref="1"/></doc>}
        {<doc><item/><item/></doc>}
    } {
        lappend result [postValidation s $xml]
    }
    s delete
    set result
} {1 0 1 0}

test schema-20.2a {domunique} {
    tdom::schema s
    s define {
        defelement doc {
            domunique item @ref itemrefkey IGNORE_EMPTY_FIELD_SET
            element item * {
                attribute ref ?
            }
        }
    }
    set result [list]
    foreach xml {
        {<doc><item ref="1"/><item ref="foo"/></doc>}
        {<doc><item ref="1"/><item ref="1"/></doc>}
        {<doc><item/><item ref="1"/></doc>}
        {<doc><item/><item/></doc>}
    } {
        lappend result [postValidation s $xml]
    }
    s delete
    set result
} {1 0 1 1}

test schema-20.3 {domunique} {
    tdom::schema s
    s define {
        defelement doc {
            element items * {
                element item * {
                    attribute ref ?
                }
                domunique item @ref
            }
        }
    }
    set result [list]
    foreach xml {
        {<doc><items><item ref="1"/><item ref="foo"/></items></doc>}
        {<doc><items><item ref="1"/><item ref="1"/></items></doc>}
        {<doc><items><item ref="1"/><item ref="foo"/></items><items><item ref="1"/><item ref="foo"/></items></doc>}
        {<doc><items><item ref="1"/><item ref="foo"/></items><items><item ref="1"/><item ref="bar"/></items></doc>}
        {<doc><items><item ref="1"/><item ref="foo"/></items><items><item ref="2"/><item ref="bar"/></items></doc>}
        {<doc><items><item ref="1"/><item ref="foo"/></items><items><item ref="bar"/><item ref="bar"/></items></doc>}
    } {
        lappend result [postValidation s $xml]
    }
    s delete
    set result
} {1 0 1 1 1 0}

test schema-20.4 {domunique} {
    tdom::schema s
    s define {
        defelement doc {
            domunique item {@ref @id}
            element item * {
                attribute ref ?
                attribute id ?
            }
        }
    }
    set result [list]
    foreach xml {
        {<doc><item ref="1"/><item ref="foo"/></doc>}
        {<doc><item ref="1"/><item ref="1"/></doc>}
        {<doc><item ref="1" id="a"/><item ref="foo" id="a"/></doc>}
        {<doc><item ref="1" id="a"/><item ref="1" id="a"/></doc>}
        {<doc><item ref="1" id="a"/><item ref="1" id="b"/></doc>}
    } {
        lappend result [postValidation s $xml]
    }
    s delete
    set result
} {1 0 1 0 1}

}