tDOM

Check-in [ba6bd2bb7c]
Login

Check-in [ba6bd2bb7c]

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

Overview
Comment: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).
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | schema
Files: files | file ages | folders
SHA3-256: ba6bd2bb7cc770b410016a7729f73fcb1d8df7362eeac1f66f48308aef750d3e
User & Date: rolf 2019-05-21 22:30:10.661
Context
2019-05-31
00:05
Added sub tree local uniqueness and foreign key contents. check-in: 43cf45b772 user: rolf tags: schema
2019-05-25
00:46
Started work. (Wenn es anders nicht geht, so geht es doch viel besser.) check-in: 11c1cd9dad user: rolf tags: keyspaces
2019-05-21
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
22:24
Added optional flag argument to domunique. Currently only flag controls, if empty field node sets are ignored (or seen as the empty string). Closed-Leaf check-in: 6a51d9897b user: rolf tags: domlocalkey
2019-05-14
20:02
Enhanced the text constraint commands id/idref: Beside the one doc wide ID space there are now additional other named doc wide ID/IDREF spaces possible. Each of them work along the unnamed doc wide ID space. check-in: 6b550b98f8 user: rolf tags: schema
Changes
Unified Diff 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

#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 







<







38
39
40
41
42
43
44

45
46
47
48
49
50
51

#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 
699
700
701
702
703
704
705






706
707
708
709
710
711
712

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







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








>
>
>
>
>
>







698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717

/*--------------------------------------------------------------------------
|   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
        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;







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







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
        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;
5222
5223
5224
5225
5226
5227
5228





























































5229
5230
5231
5232
5233
5234
5235
    CHECK_RC;

    DBG(rsPrint( result );)
    return 0;

} /* xpathEval */






























































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







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







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
    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.
21
22
23
24
25
26
27

28
29
30
31
32
33
34
|
\---------------------------------------------------------------------------*/

#ifndef TDOM_NO_SCHEMA

#include <tdom.h>
#include <tcldom.h>

#include <schema.h>

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







>







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
\---------------------------------------------------------------------------*/

#ifndef TDOM_NO_SCHEMA

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

/* #define DEBUG */
/* #define DDEBUG */
/*----------------------------------------------------------------------------
|   Debug Macros
|
81
82
83
84
85
86
87







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    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 SetResult(str) Tcl_ResetResult(interp); \
                     Tcl_SetStringObj(Tcl_GetObjResult(interp), (str), -1)
#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)







>
>
>
>
>
>
>
















|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    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 SetResult(str) Tcl_ResetResult(interp); \
                     Tcl_SetStringObj(Tcl_GetObjResult(interp), (str), -1)
#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)
368
369
370
371
372
373
374





















375
376
377
378
379
380
381
    }
    fprintf (stderr, "++++ Stack bottom\n");
}
)

/* DBG end */






















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








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







376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    }
    fprintf (stderr, "++++ Stack bottom\n");
}
)

/* DBG end */


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;

403
404
405
406
407
408
409

410
411
412
413
414
415
416
        FREE (pattern->quants);
        if (pattern->attrs) {
            for (i = 0; i < pattern->numAttr; i++) {
                FREE (pattern->attrs[i]);
            }
            FREE (pattern->attrs);
        }

        break;
    }
    FREE (pattern);
}

static SchemaData*
initSchemaData (







>







432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
        FREE (pattern->quants);
        if (pattern->attrs) {
            for (i = 0; i < pattern->numAttr; i++) {
                FREE (pattern->attrs[i]);
            }
            FREE (pattern->attrs);
        }
        freedomKeyConstraints (pattern->domKeys);
        break;
    }
    FREE (pattern);
}

static SchemaData*
initSchemaData (
2055
2056
2057
2058
2059
2060
2061





























































































































































































































2062
2063
2064
2065
2066
2067
2068
    }
    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
    )







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







2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
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
    }
    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
    )
2104
2105
2106
2107
2108
2109
2110





2111
2112
2113
2114
2115
2116
2117
            /* 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)







>
>
>
>
>







2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
            /* 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)
3410
3411
3412
3413
3414
3415
3416



















































































3417
3418
3419
3420
3421
3422
3423
        pattern->content[i-1] = (SchemaCP *) objv[i];
        Tcl_IncrRefCount (objv[i]);
    }
    pattern->nc = objc;
    addToContent (sdata, pattern, SCHEMA_CQUANT_ONE, 0, 0);
    return TCL_OK;
}




















































































static int
integerImpl (
    Tcl_Interp *interp,
    void *constraintData,
    char *text
    )







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







3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
        pattern->content[i-1] = (SchemaCP *) objv[i];
        Tcl_IncrRefCount (objv[i]);
    }
    pattern->nc = objc;
    addToContent (sdata, pattern, SCHEMA_CQUANT_ONE, 0, 0);
    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
    )
4712
4713
4714
4715
4716
4717
4718




4719
4720
4721
4722
4723
4724
4725
                          NamespacePatternObjCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::text",
                          TextPatternObjCmd, NULL, NULL);

    /* The 'virtual' "tcl" definition command */
    Tcl_CreateObjCommand (interp, "tdom::schema::tcl",
                          VirtualPatternObjCmd, 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",







>
>
>
>







5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
                          NamespacePatternObjCmd, NULL, NULL);
    Tcl_CreateObjCommand (interp, "tdom::schema::text",
                          TextPatternObjCmd, NULL, NULL);

    /* The 'virtual' "tcl" definition command */
    Tcl_CreateObjCommand (interp, "tdom::schema::tcl",
                          VirtualPatternObjCmd, 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",
Changes to generic/schema.h.
20
21
22
23
24
25
26



27
28
29
30
31
32
33
|   Nov, Dec 2018
|
\---------------------------------------------------------------------------*/

#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,







>
>
>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|   Nov, Dec 2018
|
\---------------------------------------------------------------------------*/

#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,
67
68
69
70
71
72
73









74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
/* 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 SchemaCP
{
    Schema_CP_Type    type;
    char             *namespace;
    char             *name;
    struct SchemaCP  *next;
    SchemaFlags       flags;
    struct SchemaCP **content;
    SchemaQuant      *quants;
    unsigned int      nc;
    SchemaAttr      **attrs;
    unsigned int      numAttr;
    unsigned int      numReqAttr;

} SchemaCP;

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







>
>
>
>
>
>
>
>
>














>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/* 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 struct SchemaCP
{
    Schema_CP_Type    type;
    char             *namespace;
    char             *name;
    struct SchemaCP  *next;
    SchemaFlags       flags;
    struct SchemaCP **content;
    SchemaQuant      *quants;
    unsigned int      nc;
    SchemaAttr      **attrs;
    unsigned int      numAttr;
    unsigned int      numReqAttr;
    domKeyConstraint *domKeys;
} SchemaCP;

typedef struct SchemaValidationStack
{
    SchemaCP *pattern;
    struct SchemaValidationStack *next;
    struct SchemaValidationStack *down;
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

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;







|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

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.
4607
4608
4609
4610
4611
4612
4613








































































































































4614
4615
4616

proc postValidation {g xml} {
    set doc [dom parse $xml]
    set rc [$g domvalidate $doc errMsg]
    $doc delete
    return $rc
}










































































































































}







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



4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752

proc postValidation {g xml} {
    set doc [dom parse $xml]
    set rc [$g domvalidate $doc errMsg]
    $doc delete
    return $rc
}

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}


}