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

Overview
Comment:Integrated further improvements and features to recovering.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | schema
Files: files | file ages | folders
SHA3-256: 0e98933aa6179e2e808c65f8909e017e492f0d84d6f0aba1091ae87d35870052
User & Date: rolf 2020-07-30 14:33:15
Context
2020-07-31
10:59
Minor documentation and test suite work. check-in: a14c1bb515 user: rolf tags: schema
2020-07-30
14:33
Integrated further improvements and features to recovering. check-in: 0e98933aa6 user: rolf tags: schema
13:16
Respect "ignore" return value from recover script in case of END_EVENT, enabling to get further recover calls for other missing mandantory content particle of the current content model. Closed-Leaf check-in: 42c54de7e7 user: rolf tags: wip
2020-07-23
23:50
Merged from trunk. check-in: 76ba31355c user: rolf tags: schema
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to doc/schema.xml.

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
229
230
231
232
233
234
235


236
237
238
239
240
241
242
....
1190
1191
1192
1193
1194
1195
1196
1197
1198

































1199
1200
1201
1202
1203
1204
1205
    schema.</p>

    <p>Additionally, a validation command may be used as argument to
    the <m>-validateCmd</m> option of the <m>dom parse</m> and the
    <m>expat</m> commands to enable validation additional to what they
    otherwise do.</p>

    <p>The valid methods of the created commands are:</p>

    <commandlist>
      <commanddef>
          <command><method>prefixns</method> <m>?prefixUriList?</m></command>
          <desc>This method gives control to a prefix (or
          abbreviation) to namespace URI mapping. Everywhere a
          namespace argument is expected in the schema command methods
................................................................................
        <desc>Returns true if the first argument is a valid tree or
        false otherwise. If validation failed and the optional
        <m>objVar</m> argument is given, then the variable with that
        name is set to a validation error message. If the dom tree is
        valid and the optional <m>objVar</m> argument is given, then
        the variable with that name is set to the empty string. </desc>
      </commanddef>
      
      <commanddef>
        <command><method>reportcmd</method> <m>?cmdName?</m></command>
        <desc>This method expects the name of a tcl command to be
        called in case of validation error. This command will be
        called with two arguments: the schema command, that is rasing
        the validation error, and a validation error code. For more
        detailed information see <ref
................................................................................
            <dt>DOM_KEYCONSTRAINT</dt><dd></dd>
            <dt>DOM_XPATH_BOOLEAN</dt><dd></dd>
            <dt>INVALID_KEYREF</dt><dd></dd>
            <dt>INVALID_VALUE</dt><dd></dd>
            <dt>UNKOWN_GLOBAL_ID</dt><dd></dd>
            <dt>UNKOWN_ID</dt><dd></dd>
        </dl>


        </desc>
      </commanddef>

      <commanddef>
        <command><method>delete</method></command>
        <desc>This method deletes the validation command.</desc>
      </commanddef>
................................................................................
    </commandlist>
    
  </section>

  <section id="recovering">
      <title>Recovering</title>

      <p>
      </p>

































  </section>

  <section>
    <title>Exampels</title>

    <p>The XML Schema Part 0: Primer Second Edition
    (<url>https://www.w3.org/TR/xmlschema-0/</url>) starts with this







|







 







|







 







>
>







 







|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
....
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
    schema.</p>

    <p>Additionally, a validation command may be used as argument to
    the <m>-validateCmd</m> option of the <m>dom parse</m> and the
    <m>expat</m> commands to enable validation additional to what they
    otherwise do.</p>

    <p>The methods of the created commands are:</p>

    <commandlist>
      <commanddef>
          <command><method>prefixns</method> <m>?prefixUriList?</m></command>
          <desc>This method gives control to a prefix (or
          abbreviation) to namespace URI mapping. Everywhere a
          namespace argument is expected in the schema command methods
................................................................................
        <desc>Returns true if the first argument is a valid tree or
        false otherwise. If validation failed and the optional
        <m>objVar</m> argument is given, then the variable with that
        name is set to a validation error message. If the dom tree is
        valid and the optional <m>objVar</m> argument is given, then
        the variable with that name is set to the empty string. </desc>
      </commanddef>

      <commanddef>
        <command><method>reportcmd</method> <m>?cmdName?</m></command>
        <desc>This method expects the name of a tcl command to be
        called in case of validation error. This command will be
        called with two arguments: the schema command, that is rasing
        the validation error, and a validation error code. For more
        detailed information see <ref
................................................................................
            <dt>DOM_KEYCONSTRAINT</dt><dd></dd>
            <dt>DOM_XPATH_BOOLEAN</dt><dd></dd>
            <dt>INVALID_KEYREF</dt><dd></dd>
            <dt>INVALID_VALUE</dt><dd></dd>
            <dt>UNKOWN_GLOBAL_ID</dt><dd></dd>
            <dt>UNKOWN_ID</dt><dd></dd>
        </dl>
        <p> For more detailed information see section <ref
        refid="recovering">Recovering</ref>.</p>
        </desc>
      </commanddef>

      <commanddef>
        <command><method>delete</method></command>
        <desc>This method deletes the validation command.</desc>
      </commanddef>
................................................................................
    </commandlist>
    
  </section>

  <section id="recovering">
      <title>Recovering</title>

      <p>By default the validation engine stops at the first detected
      validation violation and reports that finding. It does so by
      result false (and set, if given, the result variable with an
      error message) in case the schema command itself is used to
      validate input. If the schema command is used by a SAX parser or
      the DOM parser it does so by throwing error.</p>

      <p>If a <m>reportcmd</m> is set this command is called on global
      level appended with the schema command and an error type as
      arguments if a validation violation is detected. Then the
      validation recovers from the error and continues. For some
      validation errors the reocver strategy can be determined with
      the script result of the reportcmd.</p>

      <p>With a <m>reportcmd</m> (which doesen&amp;t throw error if
      called) the validation engine will never report validation
      failure to its caller. The validation engine recovers, continues
      and report the next error, if one happen and so until the end of
      the input. The schema command will return true and the SAX
      parser and DOM builder will process normally until the end of
      the input, as if there would not have been validation.</p>

      <p>Please note, that this happens only for validation errors. It
      isn't possible to recover from well-formedness errors. If the
      input isn't well-formed the schema command returns false and
      set, if given, the result variable with an error message about
      the well-formedness error.</p>

      <p>If the <m>reportcmd</m> throws error while called by the
      validation engine then validation stops and the schema command
      thows error with the error message of the script.</p>

      

      
  </section>

  <section>
    <title>Exampels</title>

    <p>The XML Schema Part 0: Primer Second Edition
    (<url>https://www.w3.org/TR/xmlschema-0/</url>) starts with this

Changes to generic/schema.c.

192
193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
...
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
...
794
795
796
797
798
799
800
801



802
803
804
805
806
807
808
809
810
811
812
813
814
815
...
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
....
1230
1231
1232
1233
1234
1235
1236



1237

1238
1239
1240
1241
1242
1243
1244
....
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
....
1626
1627
1628
1629
1630
1631
1632












1633
1634
1635
1636
1637
1638
1639
....
2187
2188
2189
2190
2191
2192
2193















2194
2195
2196
2197
2198
2199
2200
....
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
....
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
....
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
....
2465
2466
2467
2468
2469
2470
2471

2472
2473
2474
2475
2476
2477
2478




2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493


2494
2495
2496
2497
2498
2499
2500
    "UNKOWN_GLOBAL_ID",
    "UNKOWN_ID",
    "INVALID_ATTRIBUTE_VALUE",
    "INVALID_VALUE"
};

/*----------------------------------------------------------------------------
|   Recover related flage
|
\---------------------------------------------------------------------------*/

#define RECOVER_FLAG_REWIND 1
#define RECOVER_FLAG_DONT_REPORT 2
#define RECOVER_FLAG_IGNORE 4


/*----------------------------------------------------------------------------
|   [schemacmd info expected] related flags
|
\---------------------------------------------------------------------------*/

#define EXPECTED_IGNORE_MATCHED 1
................................................................................
        ac += + 1;                                \
        hm = 0;                                   \
    }                                             \


#define updateStack(sdata,se,ac)                        \
    if (!(sdata->recoverFlags & RECOVER_FLAG_REWIND)) { \
        se->activeChild = ac;                 \
        se->hasMatched = 1;                   \
    }                                                   \


#ifndef TCL_THREADS
SchemaData *
tdomGetSchemadata (Tcl_Interp *interp) 
{
................................................................................
    for (i = from; i < sdata->numPatternList; i++) {
        this = sdata->patternList[i];
        hashTable = NULL;
        name = NULL;
        if (this->type == SCHEMA_CTYPE_NAME) {
            /* Local defined  elements aren't saved under  their local
             * name bucket in the sdata->element hash table. */
            if (!(this->flags & LOCAL_DEFINED_ELEMENT)) {



                if (this->flags & ELEMENTTYPE_DEF) {
                    hashTable = &sdata->elementType;
                    name = this->typeName;
                } else {
                    hashTable = &sdata->element;
                    name = this->name;
                }
            }
        }
        if (this->type == SCHEMA_CTYPE_PATTERN) {
            hashTable = &sdata->pattern;
            name = this->name;
        }
        if (name && hashTable) {
................................................................................
static void
pushToStack (
    SchemaData *sdata,
    SchemaCP *pattern
    )
{
    SchemaValidationStack *se, *nextse;

    DBG(fprintf(stderr, "push to Stack:\n");serializeCP(pattern));
    if (pattern->type == SCHEMA_CTYPE_NAME && sdata->lastMatchse) {
        se = sdata->lastMatchse;
        while (se) {
            nextse = se->down;
            repoolStackElement (sdata, se);
            se = nextse;
................................................................................
        }
        break;
    case UNEXPECTED_TEXT:
        sdata->recoverFlags |= RECOVER_FLAG_REWIND;
        break;
    case MISSING_ELEMENT_MATCH_END:
    case MISSING_TEXT_MATCH_END:



        sdata->recoverFlags |= RECOVER_FLAG_DONT_REPORT;

        break;        
    case DOM_KEYCONSTRAINT:
    case DOM_XPATH_BOOLEAN:
    case MISSING_ATTRIBUTE:
    case MISSING_TEXT_MATCH_START:
    case UNEXPECTED_ROOT_ELEMENT:
    case UNKNOWN_ATTRIBUTE:
................................................................................
                    }
                } else {
                    thismayskip = 1;
                }
                break;

            case SCHEMA_CTYPE_ANY:
                if (icp->namespace && icp->namespace == namespace) {
                    break;
                }
                sdata->skipDeep = 1;
                se->hasMatched = 1;
                se->interleaveState[i] = 1;
                /* See comment in probeElement: sdata->vname and
                 * sdata->vns may be pre-filled. We reset it here.*/
................................................................................

            }
            if (!thismayskip && minOne (cp->quants[i])) mayskip = 0;
        }
        if (mayskip) break;
        if (recover (interp, sdata, MISSING_ELEMENT_MATCH_START, name,
                     namespace, NULL, cp->nc)) {












            return 1;
        }
        return 0;
    }
    return -1;
}

................................................................................
    if (reqAttr != cp->numReqAttr) {
        SetResult ("Missing mandatory attribute(s)");
        return TCL_ERROR;
    }
    return TCL_OK;
}
















static int checkElementEnd (
    Tcl_Interp *interp,
    SchemaData *sdata
    )
{
    SchemaValidationStack *se;
    SchemaCP *cp, *ic;
................................................................................
        if (ac < cp->nc && (hasMatched (cp->quants[ac], hm))) {
            DBG(fprintf (stderr, "ac %d has matched, skiping to next ac\n", ac));
            ac++; hm = 0;
        }
        while (ac < cp->nc) {
            DBG(fprintf (stderr, "ac %d hm %d mayMiss: %d\n",
                         ac, hm, mayMiss (cp->quants[ac])));
            if (se->interleaveState) {
                if (se->interleaveState[ac]) {
                    ac++; continue;
                }
            }
            if (mayMiss (cp->quants[ac])) {
                ac++; continue;
            }
            switch (cp->content[ac]->type) {
            case SCHEMA_CTYPE_KEYSPACE_END:
                /* Don't happen as INTERLEAVE child */
................................................................................
                        if (recursivePattern (se, ic)) {
                            thismayskip = 1;
                            break;
                        }
                        /* fall throu */
                    case SCHEMA_CTYPE_INTERLEAVE:
                        pushToStack (sdata, ic);
                        if (checkElementEnd (interp, sdata)) {
                            thismayskip = 1;
                        }
                        popStack (sdata);
                        break;
                        
                    case SCHEMA_CTYPE_KEYSPACE_END:
                    case SCHEMA_CTYPE_KEYSPACE:
................................................................................
                    if (thismayskip) break;
                }
                if (thismayskip) break;
                if (!recover (interp, sdata, MISSING_ELEMENT_MATCH_END, NULL,
                              NULL, NULL, 0)) {
                    return 0;
                }




                break;
                
            case SCHEMA_CTYPE_VIRTUAL:
                if (evalVirtual (interp, sdata, ac)) break;
                else return 0;
                
            case SCHEMA_CTYPE_PATTERN:
................................................................................
                if (recursivePattern (se, cp->content[ac])) {
                    break;
                }
                /* fall throu */
            case SCHEMA_CTYPE_INTERLEAVE:
                pushToStack (sdata, cp->content[ac]);
                rc = checkElementEnd (interp, sdata);

                popStack (sdata);
                if (rc) break;



                if (recover (interp, sdata, MISSING_ELEMENT_MATCH_END,
                             NULL, NULL, NULL, 0)) {





                    break;
                }

                return 0;







                
            case SCHEMA_CTYPE_ANY:
            case SCHEMA_CTYPE_NAME:
                if (recover (interp, sdata, MISSING_ELEMENT_MATCH_END,
                             NULL, NULL, NULL, 0)) {




                    break;
                }
                return 0;
            }
            ac++;
        }
        if (se->interleaveState) {
................................................................................
        SetResult ("No validation started");
        return TCL_ERROR;
    }
    if (sdata->validationState == VALIDATION_ERROR) {
        return TCL_ERROR;
    }


    rc = checkElementEnd (interp, sdata);
    while (rc == -1) {
        popStack (sdata);
        rc = checkElementEnd (interp, sdata);
    }
    sdata->recoverFlags &= ~RECOVER_FLAG_DONT_REPORT;
    if (rc) {




        popStack (sdata);
        if (sdata->stack == NULL) {
            /* End of the first pattern (the tree root) without error. */
            /* Check for unknown ID references */
            if (!checkDocKeys (interp, sdata)) {
                return TCL_ERROR;
            }
            /*  We have successfully finished validation */
            sdata->validationState = VALIDATION_FINISHED;
        }
        DBG(
            fprintf(stderr, "probeElementEnd: _CAN_ end here.\n");
            serializeStack (sdata);
            );
        return TCL_OK;


    }
    SetResultV ("Missing mandatory content");
    sdata->validationState = VALIDATION_ERROR;
    DBG(
        fprintf(stderr, "probeElementEnd: CAN'T end here.\n");
        serializeStack (sdata);
        );







|






>







 







|
|







 







|
>
>
>
|
|
|
|
|
|
<







 







<







 







>
>
>
|
>







 







|







 







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







 







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







 







|
<
|
<







 







|







 







>
>
>
>







 







>
|
<
>
>
>
|
|
>
>
>
>
>
|
|
>
|
>
>
>
>
>
>
>





>
>
>
>







 







>
|
|
|
|
|
|
|
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>







192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
...
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811

812
813
814
815
816
817
818
...
976
977
978
979
980
981
982

983
984
985
986
987
988
989
....
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
....
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
....
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
....
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
....
2251
2252
2253
2254
2255
2256
2257
2258

2259

2260
2261
2262
2263
2264
2265
2266
....
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
....
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
....
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
    "UNKOWN_GLOBAL_ID",
    "UNKOWN_ID",
    "INVALID_ATTRIBUTE_VALUE",
    "INVALID_VALUE"
};

/*----------------------------------------------------------------------------
|   Recovering related flage
|
\---------------------------------------------------------------------------*/

#define RECOVER_FLAG_REWIND 1
#define RECOVER_FLAG_DONT_REPORT 2
#define RECOVER_FLAG_IGNORE 4
#define RECOVER_FLAG_MATCH_END_CONTINUE 8

/*----------------------------------------------------------------------------
|   [schemacmd info expected] related flags
|
\---------------------------------------------------------------------------*/

#define EXPECTED_IGNORE_MATCHED 1
................................................................................
        ac += + 1;                                \
        hm = 0;                                   \
    }                                             \


#define updateStack(sdata,se,ac)                        \
    if (!(sdata->recoverFlags & RECOVER_FLAG_REWIND)) { \
        se->activeChild = ac;                           \
        se->hasMatched = 1;                             \
    }                                                   \


#ifndef TCL_THREADS
SchemaData *
tdomGetSchemadata (Tcl_Interp *interp) 
{
................................................................................
    for (i = from; i < sdata->numPatternList; i++) {
        this = sdata->patternList[i];
        hashTable = NULL;
        name = NULL;
        if (this->type == SCHEMA_CTYPE_NAME) {
            /* Local defined  elements aren't saved under  their local
             * name bucket in the sdata->element hash table. */
            if (this->flags & LOCAL_DEFINED_ELEMENT) {
                freeSchemaCP (sdata->patternList[i]);
                continue;
            }
            if (this->flags & ELEMENTTYPE_DEF) {
                hashTable = &sdata->elementType;
                name = this->typeName;
            } else {
                hashTable = &sdata->element;
                name = this->name;

            }
        }
        if (this->type == SCHEMA_CTYPE_PATTERN) {
            hashTable = &sdata->pattern;
            name = this->name;
        }
        if (name && hashTable) {
................................................................................
static void
pushToStack (
    SchemaData *sdata,
    SchemaCP *pattern
    )
{
    SchemaValidationStack *se, *nextse;

    DBG(fprintf(stderr, "push to Stack:\n");serializeCP(pattern));
    if (pattern->type == SCHEMA_CTYPE_NAME && sdata->lastMatchse) {
        se = sdata->lastMatchse;
        while (se) {
            nextse = se->down;
            repoolStackElement (sdata, se);
            se = nextse;
................................................................................
        }
        break;
    case UNEXPECTED_TEXT:
        sdata->recoverFlags |= RECOVER_FLAG_REWIND;
        break;
    case MISSING_ELEMENT_MATCH_END:
    case MISSING_TEXT_MATCH_END:
        if (strcmp (Tcl_GetStringResult (interp), "ignore") == 0) {
            sdata->recoverFlags |= RECOVER_FLAG_MATCH_END_CONTINUE;
        } else {
            sdata->recoverFlags |= RECOVER_FLAG_DONT_REPORT;
        }
        break;        
    case DOM_KEYCONSTRAINT:
    case DOM_XPATH_BOOLEAN:
    case MISSING_ATTRIBUTE:
    case MISSING_TEXT_MATCH_START:
    case UNEXPECTED_ROOT_ELEMENT:
    case UNKNOWN_ATTRIBUTE:
................................................................................
                    }
                } else {
                    thismayskip = 1;
                }
                break;

            case SCHEMA_CTYPE_ANY:
                if (icp->namespace && icp->namespace != namespace) {
                    break;
                }
                sdata->skipDeep = 1;
                se->hasMatched = 1;
                se->interleaveState[i] = 1;
                /* See comment in probeElement: sdata->vname and
                 * sdata->vns may be pre-filled. We reset it here.*/
................................................................................

            }
            if (!thismayskip && minOne (cp->quants[i])) mayskip = 0;
        }
        if (mayskip) break;
        if (recover (interp, sdata, MISSING_ELEMENT_MATCH_START, name,
                     namespace, NULL, cp->nc)) {
            if (sdata->recoverFlags & RECOVER_FLAG_IGNORE) {
                /* We mark the first so far not matched mandatory
                 * interleave child cp as matched */
                for (i = 0; i < cp->nc; i++) {
                    if (!se->interleaveState[i]) {
                        if (minOne (cp->quants[i])) {
                            se->interleaveState[i] = 1;
                            break;
                        }
                    }
                }
            }
            return 1;
        }
        return 0;
    }
    return -1;
}

................................................................................
    if (reqAttr != cp->numReqAttr) {
        SetResult ("Missing mandatory attribute(s)");
        return TCL_ERROR;
    }
    return TCL_OK;
}

/* Returns either -1, 0, 1, 2

   -1 means a pattern or an interleave ended may end, look further at
   parents next sibling.

   0 means rewind with validation error.

   1 means element content may end here.

   2 means recovering requested further error reporting about missing childs
   in the current element. To be able to to answer a [info expected] on
   the occasion of the next error, we update the stack in this case
   and let probeElementEnd restart checkElementEnd again with this
   stack state.
*/
static int checkElementEnd (
    Tcl_Interp *interp,
    SchemaData *sdata
    )
{
    SchemaValidationStack *se;
    SchemaCP *cp, *ic;
................................................................................
        if (ac < cp->nc && (hasMatched (cp->quants[ac], hm))) {
            DBG(fprintf (stderr, "ac %d has matched, skiping to next ac\n", ac));
            ac++; hm = 0;
        }
        while (ac < cp->nc) {
            DBG(fprintf (stderr, "ac %d hm %d mayMiss: %d\n",
                         ac, hm, mayMiss (cp->quants[ac])));
            if (se->interleaveState && se->interleaveState[ac]) {

                ac++; continue;

            }
            if (mayMiss (cp->quants[ac])) {
                ac++; continue;
            }
            switch (cp->content[ac]->type) {
            case SCHEMA_CTYPE_KEYSPACE_END:
                /* Don't happen as INTERLEAVE child */
................................................................................
                        if (recursivePattern (se, ic)) {
                            thismayskip = 1;
                            break;
                        }
                        /* fall throu */
                    case SCHEMA_CTYPE_INTERLEAVE:
                        pushToStack (sdata, ic);
                        if (checkElementEnd (interp, sdata) == -1) {
                            thismayskip = 1;
                        }
                        popStack (sdata);
                        break;
                        
                    case SCHEMA_CTYPE_KEYSPACE_END:
                    case SCHEMA_CTYPE_KEYSPACE:
................................................................................
                    if (thismayskip) break;
                }
                if (thismayskip) break;
                if (!recover (interp, sdata, MISSING_ELEMENT_MATCH_END, NULL,
                              NULL, NULL, 0)) {
                    return 0;
                }
                if (sdata->recoverFlags & RECOVER_FLAG_MATCH_END_CONTINUE) {
                    updateStack (sdata, se, ac);
                    return 2;
                }
                break;
                
            case SCHEMA_CTYPE_VIRTUAL:
                if (evalVirtual (interp, sdata, ac)) break;
                else return 0;
                
            case SCHEMA_CTYPE_PATTERN:
................................................................................
                if (recursivePattern (se, cp->content[ac])) {
                    break;
                }
                /* fall throu */
            case SCHEMA_CTYPE_INTERLEAVE:
                pushToStack (sdata, cp->content[ac]);
                rc = checkElementEnd (interp, sdata);
                if (rc == 0) {
                    popStack (sdata);

                    if (sdata->stack->pattern->type == SCHEMA_CTYPE_NAME
                        || sdata->stack->activeChild
                        || sdata->stack->hasMatched) {
                        if (recover (interp, sdata, MISSING_ELEMENT_MATCH_END,
                                     NULL, NULL, NULL, 0)) {
                            if (sdata->recoverFlags &
                                RECOVER_FLAG_MATCH_END_CONTINUE) {
                                updateStack (sdata, se, ac);
                                return 2;
                            }
                            break;
                        }
                    }
                    return 0;
                }
                if (rc == 2) {
                    updateStack (sdata, se, ac);
                    return 2;
                }
                popStack (sdata);
                break;
                
            case SCHEMA_CTYPE_ANY:
            case SCHEMA_CTYPE_NAME:
                if (recover (interp, sdata, MISSING_ELEMENT_MATCH_END,
                             NULL, NULL, NULL, 0)) {
                    if (sdata->recoverFlags & RECOVER_FLAG_MATCH_END_CONTINUE) {
                        updateStack (sdata, se, ac);
                        return 2;
                    }
                    break;
                }
                return 0;
            }
            ac++;
        }
        if (se->interleaveState) {
................................................................................
        SetResult ("No validation started");
        return TCL_ERROR;
    }
    if (sdata->validationState == VALIDATION_ERROR) {
        return TCL_ERROR;
    }

    while (1) {
        rc = checkElementEnd (interp, sdata);
        while (rc == -1) {
            popStack (sdata);
            rc = checkElementEnd (interp, sdata);
        }
        sdata->recoverFlags &= ~RECOVER_FLAG_DONT_REPORT;
        if (rc == 2) {
            sdata->recoverFlags &= ~RECOVER_FLAG_MATCH_END_CONTINUE;
            continue;
        }
        if (rc == 1) {
            popStack (sdata);
            if (sdata->stack == NULL) {
                /* End of the first pattern (the tree root) without error. */
                /* Check for unknown ID references */
                if (!checkDocKeys (interp, sdata)) {
                    return TCL_ERROR;
                }
                /*  We have successfully finished validation */
                sdata->validationState = VALIDATION_FINISHED;
            }
            DBG(
                fprintf(stderr, "probeElementEnd: _CAN_ end here.\n");
                serializeStack (sdata);
                );
            return TCL_OK;
        }
        break;
    }
    SetResultV ("Missing mandatory content");
    sdata->validationState = VALIDATION_ERROR;
    DBG(
        fprintf(stderr, "probeElementEnd: CAN'T end here.\n");
        serializeStack (sdata);
        );

Changes to tests/schema.test.

3890
3891
3892
3893
3894
3895
3896























3897
3898
3899
3900
3901
3902
3903
....
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
....
8123
8124
8125
8126
8127
8128
8129












































































8130
8131
8132
8133
8134
8135
8136
....
8715
8716
8717
8718
8719
8720
8721
8722
















8723
8724
8725
8726
8727
8728
8729
        {<n:doc xmlns:n="ns1"><elm1 xmlns="ns2"/><n:elm2/></n:doc>}
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {0 0 0 0 1}
























test schema-14.1 {text: integer} {
    tdom::schema s
    s defelement doc {
        text {
            integer
        }
................................................................................
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {0 1 1 1 1 1 1 1 0 1}

test schema-16.18 {interleave with all child cp optional} {
    tdom::schema s
    s defelement doc {
        interleave {
            element a ?
            element b ?
            choice {
                element c ?
................................................................................
        ref thatPattern ?
    }
    set result [lsort -index 0 [grammar info definedPatterns]]
    grammar delete
    set result
} {{thatPattern someNamespace} thisPattern}













































































proc schema-18 {args} {
    lappend ::result {*}$args
}
test schema-18.1 {reportcmd} {
    tdom::schema s
    s define {
        defelement doc {
................................................................................
    s reportcmd ""
    lappend result [s reportcmd]
    s reportcmd dummycallback
    lappend result [s reportcmd]
    s delete
    set result
} {{} {} dummycallback {} dummycallback}
    
















proc validatedSAX {g xml {keepEmpties 1}} {
    set args [list -validateCmd $g]
    if {!$keepEmpties} {
        lappend args -ignorewhitespace 1
    }
    xml::parser p {*}$args
    set rc [catch {p parse $xml} errMsg]







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







 







|







 







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







 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
....
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
....
8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
....
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
        {<n:doc xmlns:n="ns1"><elm1 xmlns="ns2"/><n:elm2/></n:doc>}
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {0 0 0 0 1}

test schema-13.3 {Not namespaced elements inside namespaced ones} {
    tdom::schema s
    s define {
        defelement doc ns1 {
            namespace "" {
                element e
            }
        }
        defelement e {text {fixed "here"}}
    }
    set result ""
    foreach xml {
        {<doc xmlns="ns1"><e xmlns="">here</e></doc>}
        {<a:doc xmlns:a="ns1"><e xmlns="">here</e></a:doc>}
        {<a:doc xmlns:a="ns1"><e>here</e></a:doc>}
        {<doc xmlns="ns1"><e>here</e></doc>}
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {1 1 1 0}

test schema-14.1 {text: integer} {
    tdom::schema s
    s defelement doc {
        text {
            integer
        }
................................................................................
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {0 1 1 1 1 1 1 1 0 1}

test schema-16.18 {interleave with all content cp optional} {
    tdom::schema s
    s defelement doc {
        interleave {
            element a ?
            element b ?
            choice {
                element c ?
................................................................................
        ref thatPattern ?
    }
    set result [lsort -index 0 [grammar info definedPatterns]]
    grammar delete
    set result
} {{thatPattern someNamespace} thisPattern}

proc schema-17.26 {scmd errorInfo} {
    global fromReportCmd
    if {$errorInfo eq "MISSING_ELEMENT" && [$scmd info vaction] eq "MATCH_ELEMENT_END"} {
        lappend fromReportCmd "END_EVENT [$scmd info vaction name]" "expecting [lsort [$scmd info expected]]"
        return "ignore"
    } else {
        lappend fromReportCmd "[$scmd info vaction] expecting [$scmd info expected]"
    }
}

test schema-17.26 {return "ignore" from recover handler for element end event} {
    tdom::schema s
    s define {
        defelement doc {
            element a
            ref bpat
            element d
        }
        defpattern bpat {
            element b
            ref c
        }
        defpattern c {
            element c +
        }
    }
    s reportcmd schema-17.26
    set result ""
    set xmlnr 0
    foreach xml {
        <doc/>
        <doc><a/></doc>
        <doc><a/><b/></doc>
        <doc><a/><b/><d/></doc>
        <doc><a/><b/><c/><d/></doc>
        <doc><a/><b/><c/><c/><c/><d/></doc>
    } {
        set ::fromReportCmd ""
        lappend result $xmlnr: [s validate $xml errMsg]
        lappend result {*}$fromReportCmd
        incr xmlnr
    }
    s delete
    set result
} {0: 1 {END_EVENT doc} {expecting a} {END_EVENT doc} {expecting b} {END_EVENT doc} {expecting d} 1: 1 {END_EVENT doc} {expecting b} {END_EVENT doc} {expecting d} 2: 1 {END_EVENT doc} {expecting c} {END_EVENT doc} {expecting d} 3: 1 {MATCH_ELEMENT_START expecting c} 4: 1 5: 1}

test schema-17.27 {return "ignore" from recover handler for element end event} {
    tdom::schema s
    s define {
        defelement doc {
            element a
            element b
            element c
            element d
        }
    }
    s reportcmd schema-17.26
    set result ""
    set xmlnr 0
    foreach xml {
        <doc/>
        <doc><a/></doc>
        <doc><a/><b/></doc>
        <doc><a/><b/><d/></doc>
        <doc><a/><b/><c/><d/></doc>
        <doc><a/><b/><c/><c/><c/><d/></doc>
    } {
        set ::fromReportCmd ""
        lappend result $xmlnr: [s validate $xml errMsg]
        lappend result {*}$fromReportCmd
        incr xmlnr
    }
    s delete
    set result
} {0: 1 {END_EVENT doc} {expecting a} {END_EVENT doc} {expecting b} {END_EVENT doc} {expecting c} {END_EVENT doc} {expecting d} 1: 1 {END_EVENT doc} {expecting b} {END_EVENT doc} {expecting c} {END_EVENT doc} {expecting d} 2: 1 {END_EVENT doc} {expecting c} {END_EVENT doc} {expecting d} 3: 1 {MATCH_ELEMENT_START expecting c} 4: 1 5: 1 {MATCH_ELEMENT_START expecting d}}

proc schema-18 {args} {
    lappend ::result {*}$args
}
test schema-18.1 {reportcmd} {
    tdom::schema s
    s define {
        defelement doc {
................................................................................
    s reportcmd ""
    lappend result [s reportcmd]
    s reportcmd dummycallback
    lappend result [s reportcmd]
    s delete
    set result
} {{} {} dummycallback {} dummycallback}

proc schema-18.22 {that scmd errorType} {
    lappend ::result $that $errorType
}  
test schema-18.22 {reportcmd with arguments} {
    tdom::schema s 
    s reportcmd "schema-18.22 this"
    s defelement doc {
        element a
        element b
    }
    set result ""
    lappend result [s validate {<doc><a/></doc>}]
    s delete
    set result
} {this MISSING_ELEMENT 1}

proc validatedSAX {g xml {keepEmpties 1}} {
    set args [list -validateCmd $g]
    if {!$keepEmpties} {
        lappend args -ignorewhitespace 1
    }
    xml::parser p {*}$args
    set rc [catch {p parse $xml} errMsg]