tDOM

Changes On Branch localkey
Login

Changes On Branch localkey

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

Changes In Branch localkey Excluding Merge-Ins

This is equivalent to a diff from ba6bd2bb7c to 66e05a9e1a

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-24
12:54
Save work. Closed-Leaf check-in: 66e05a9e1a user: rolf tags: localkey
2019-05-22
22:22
Added the XPath step "." as allowed step in restricted schema XPath Expressions. check-in: ab18265ee0 user: rolf tags: localkey
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 to generic/dom.c.

648
649
650
651
652
653
654

655
656

657
658
659
660
661
662
663
664
        while (prefixMappings[i]) {
            if (strcmp (prefix, prefixMappings[i]) == 0) {
                return prefixMappings[i+1];
            }
            i += 2;
        }
    }

    ns = domLookupPrefix (node, prefix);
    if (ns) return ns->uri;

    else    return NULL;
}

/*---------------------------------------------------------------------------
|   domLookupPrefix
|
\--------------------------------------------------------------------------*/
domNS *







>
|
|
>
|







648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
        while (prefixMappings[i]) {
            if (strcmp (prefix, prefixMappings[i]) == 0) {
                return prefixMappings[i+1];
            }
            i += 2;
        }
    }
    if (node) {
        ns = domLookupPrefix (node, prefix);
        if (ns) return ns->uri;
    }
    return NULL;
}

/*---------------------------------------------------------------------------
|   domLookupPrefix
|
\--------------------------------------------------------------------------*/
domNS *

Changes to generic/schema.c.

262
263
264
265
266
267
268








269
270
271
272
273
274
275
    if (cp->namespace) {                                \
        Tcl_SetStringObj (rObj, cp->namespace, -1);     \
        Tcl_AppendToObj (rObj, ":", 1);                 \
    }                                                   \
    Tcl_AppendToObj (rObj, cp->name, -1);

#define S(str)  str, sizeof (str) -1









static SchemaCP*
initSchemaCP (
    Schema_CP_Type type,
    void *namespace,
    char *name
    )







>
>
>
>
>
>
>
>







262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    if (cp->namespace) {                                \
        Tcl_SetStringObj (rObj, cp->namespace, -1);     \
        Tcl_AppendToObj (rObj, ":", 1);                 \
    }                                                   \
    Tcl_AppendToObj (rObj, cp->name, -1);

#define S(str)  str, sizeof (str) -1

#define getKeyState(ks) \
    if (sdata->keyStatePool) { \
        ks = sdata->keyStatePool; \
        sdata->keyStatePool = ks->next; \
    } else { \
        ks = TMALLOC (KeyState); \
    }

static SchemaCP*
initSchemaCP (
    Schema_CP_Type type,
    void *namespace,
    char *name
    )
376
377
378
379
380
381
382









































383
384
385
386
387
388
389
    }
    fprintf (stderr, "++++ Stack bottom\n");
}
)

/* DBG end */











































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







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







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
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
    }
    fprintf (stderr, "++++ Stack bottom\n");
}
)

/* DBG end */

static void freeKeyConstraints (
    KeyConstraint *kc
    )
{
    KeyConstraint *knext;
    KeyStep *step, *snext, *child, *nchild;
    int i;

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


static void freedomKeyConstraints (
    domKeyConstraint *kc
    )
{
    domKeyConstraint *knext;
    int i;
432
433
434
435
436
437
438

439
440
441
442
443
444
445
        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*







>







481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
        FREE (pattern->quants);
        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*
499
500
501
502
503
504
505


506
507
508
509
510
511
512
{
    SchemaData *sdata = (SchemaData *) clientData;
    unsigned int i;
    SchemaValidationStack *down;
    Tcl_HashEntry *h;
    Tcl_HashSearch search;
    SchemaDocKey *dk;



    /* Protect the clientData to be freed inside (even nested)
     * Tcl_Eval*() calls to avoid invalid mem access and postpone the
     * cleanup until the Tcl_Eval*() calls are finished (done in
     * schemaInstanceCmd(). */
    if (sdata->currentEvals) {
        sdata->cleanupAfterEval = 1;







>
>







549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
{
    SchemaData *sdata = (SchemaData *) clientData;
    unsigned int i;
    SchemaValidationStack *down;
    Tcl_HashEntry *h;
    Tcl_HashSearch search;
    SchemaDocKey *dk;
    KeyState *ks, *nextks;
    KeyTable *kt, *nextkt;

    /* Protect the clientData to be freed inside (even nested)
     * Tcl_Eval*() calls to avoid invalid mem access and postpone the
     * cleanup until the Tcl_Eval*() calls are finished (done in
     * schemaInstanceCmd(). */
    if (sdata->currentEvals) {
        sdata->cleanupAfterEval = 1;
564
565
566
567
568
569
570












571
572
573
574
575
576
577
         h != NULL;
         h = Tcl_NextHashEntry (&search)) {
        dk = Tcl_GetHashValue (h);
        Tcl_DeleteHashTable (&dk->ids);
        FREE (dk);
    }
    Tcl_DeleteHashTable (&sdata->idTables);












    FREE (sdata);
}

static void
cleanupLastPattern (
    SchemaData *sdata,
    unsigned int from







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







616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
         h != NULL;
         h = Tcl_NextHashEntry (&search)) {
        dk = Tcl_GetHashValue (h);
        Tcl_DeleteHashTable (&dk->ids);
        FREE (dk);
    }
    Tcl_DeleteHashTable (&sdata->idTables);
    ks = sdata->keyStatePool;
    while (ks) {
        nextks = ks->next;
        FREE (ks);
        ks = nextks;
    }
    kt = sdata->keyTablePool;
    while (kt) {
        nextkt = kt->next;
        FREE (kt);
        kt = nextkt;
    }
    FREE (sdata);
}

static void
cleanupLastPattern (
    SchemaData *sdata,
    unsigned int from
720
721
722
723
724
725
726



727
728
729
730
731
732
733
734
735
736
737
738
739
740
741




















































742
743
744
745
746
747
748
749
750
751

752
753
754
755















756
757
758
759
760
761
762
static void
pushToStack (
    SchemaData *sdata,
    SchemaCP *pattern
    )
{
    SchemaValidationStack *stackElm, *se;




    DBG(fprintf(stderr, "push to Stack:\n");serializeCP(pattern));
    if (sdata->stackPool) {
        stackElm = sdata->stackPool;
        sdata->stackPool = stackElm->down;
    } else {
        stackElm = TMALLOC (SchemaValidationStack);
    }
    memset (stackElm, 0, sizeof (SchemaValidationStack));
    se = sdata->stack;
    stackElm->down = se;
    stackElm->pattern = pattern;
    if (pattern->type == SCHEMA_CTYPE_INTERLEAVE) {
        stackElm->interleaveState = MALLOC (sizeof (int) * pattern->nc);
        memset (stackElm->interleaveState, 0, sizeof (int) * pattern->nc);




















































    }
    sdata->stack = stackElm;
}

static void
popStack (
    SchemaData *sdata
    )
{
    SchemaValidationStack *se;

    DBG(fprintf(stderr, "pop from Stack:\n");serializeCP(sdata->stack->pattern));
    if (sdata->stack->interleaveState) {
        FREE (sdata->stack->interleaveState);
        sdata->stack->interleaveState = NULL;















    }
    se = sdata->stack->down;
    sdata->stack->down = sdata->stackPool;
    sdata->stackPool = sdata->stack;
    sdata->stack = se;
}








>
>
>
|














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










>




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







784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
static void
pushToStack (
    SchemaData *sdata,
    SchemaCP *pattern
    )
{
    SchemaValidationStack *stackElm, *se;
    KeyConstraint *kc;
    KeyState *ks, *newks;
    KeyTable *kt;
    
    DBG(fprintf(stderr, "push to Stack:\n");serializeCP(pattern));
    if (sdata->stackPool) {
        stackElm = sdata->stackPool;
        sdata->stackPool = stackElm->down;
    } else {
        stackElm = TMALLOC (SchemaValidationStack);
    }
    memset (stackElm, 0, sizeof (SchemaValidationStack));
    se = sdata->stack;
    stackElm->down = se;
    stackElm->pattern = pattern;
    if (pattern->type == SCHEMA_CTYPE_INTERLEAVE) {
        stackElm->interleaveState = MALLOC (sizeof (int) * pattern->nc);
        memset (stackElm->interleaveState, 0, sizeof (int) * pattern->nc);
    }
    if (pattern->type == SCHEMA_CTYPE_NAME && se) {
        /* Handle local key contraint matches */
        ks = se->keyState;
        while (ks) {
            if (ks->selector->name != pattern->name
                || ks->selector->ns != pattern->namespace) {
                ks = ks->next;
                continue;
            }
            if (ks->selector->child) {
                getKeyState(newks);
                newks->keyTable = ks->keyTable;
                newks->ownTable = 0;
                newks->selector = ks->selector->child;
                newks->fields = ks->fields;
                newks->next = stackElm->keyState;
                stackElm->keyState = newks;
                if (ks->type == SCHEMA_STEP_DESCENDANT_ELEMENT) {
                    getKeyState(newks);
                    newks->keyTable = ks->keyTable;
                    newks->ownTable = 0;
                    newks->selector = ks->selector;
                    newks->fields = ks->fields;
                    newks->next = stackElm->keyState;
                    stackElm->keyState = newks;                    
                }
            } else {
                /* Selector has matched, grab the fields */
            }
        }
        
        /* And open new local key contraints, if necessary. */
        kc = pattern->localkeys;
        while (kc) {
            getKeyState (ks);
            if (sdata->keyTablePool) {
                kt = sdata->keyTablePool;
                sdata->keyTablePool = kt->next;
            } else {
                kt = TMALLOC (KeyTable);
            }
            Tcl_InitHashTable (&kt->values, TCL_STRING_KEYS);
            ks->keyTable = kt;
            ks->ownTable = 1;
            ks->type = kc->type;
            ks->selector = kc->selector;
            ks->fields = kc->fields;
            ks->next = stackElm->keyState;
            stackElm->keyState = ks;
            kc = kc->next;
        }
    }
    sdata->stack = stackElm;
}

static void
popStack (
    SchemaData *sdata
    )
{
    SchemaValidationStack *se;
    KeyState *ks, *nextks;
    DBG(fprintf(stderr, "pop from Stack:\n");serializeCP(sdata->stack->pattern));
    if (sdata->stack->interleaveState) {
        FREE (sdata->stack->interleaveState);
        sdata->stack->interleaveState = NULL;
    }
    if (sdata->stack->pattern->type == SCHEMA_CTYPE_NAME) {
        /* Check and cleanup local key contraints */
        ks = sdata->stack->keyState;
        while (ks) {
            nextks = ks->next;
            if (ks->ownTable) {
                ks->keyTable->next = sdata->keyTablePool;
                sdata->keyTablePool = ks->keyTable;
                Tcl_DeleteHashTable (&ks->keyTable->values);
            }
            ks->next = sdata->keyStatePool;
            sdata->keyStatePool = ks;
            ks = nextks;
        }
    }
    se = sdata->stack->down;
    sdata->stack->down = sdata->stackPool;
    sdata->stackPool = sdata->stack;
    sdata->stack = se;
}

3666
3667
3668
3669
3670
3671
3672





































































































































































































































































3673
3674
3675
3676
3677
3678
3679
        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[]







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







3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
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
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
        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;
}

extern void printAst (int depth, ast t);

static int
processSchemaXPath (
    Tcl_Interp *interp,
    SchemaData *sdata,
    KeyConstraint *kc,
    StepType nextType,
    ast t,
    int field,
    int toplevel
    )
{
    ast child, savedt;
    KeyStep *step, *curstep;
    Tcl_HashTable *table;
    Tcl_HashEntry *h;
    int rc, hnew;
    SchemaCP *cp;
    
    /* if (toplevel) printAst (0, t); */
    while (t) {
        switch (t->type) {
        case GetContextNode:
            t = t->next;
            continue;
        case CombineSets:
            child = t->child;
            if (child->type == EvalSteps) {
                savedt = NULL;
                child = child->child;
            } else {
                savedt = child->next;
                child->next = NULL;
            }
            if (field) {
                step = kc->fields[field-1];
                kc->fields[field-1] = NULL;
            } else {
                step = kc->selector;
                kc->selector = NULL;
            }
            if (!processSchemaXPath (interp, sdata, kc, SCHEMA_STEP_NONE,
                                     child, field, toplevel)) {
                if (savedt) child->next = savedt;
                if (field) kc->fields[field-1] = step;
                else kc->selector = step;
                return 0;
            }
            if (step) {
                if (field) {
                    curstep = step;
                    while (curstep->next) curstep = curstep->next;
                    curstep->next = kc->fields[field-1];
                } else {
                    curstep = step;
                    while (curstep->next) curstep = curstep->next;
                    step->next = kc->selector;
                }
            } else {
                if (field) {
                    step = kc->fields[field-1];
                } else {
                    step = kc->selector;
                }
            }
            if (field) kc->fields[field-1] = NULL;
            else kc->selector = NULL;
            if (savedt) child->next = savedt;
            child = t->child->next;
            if (child->type == EvalSteps) child = child->child;
            rc = processSchemaXPath (interp, sdata, kc, SCHEMA_STEP_NONE,
                                     child, field, toplevel);
            if (field) {
                if (kc->fields[field-1]) {
                    curstep = step;
                    while (curstep->next) curstep = curstep->next;
                    curstep->next = kc->fields[field-1];
                }
                kc->fields[field-1] = step;
            } else {
                if (kc->selector) {
                    curstep = step;
                    while (curstep->next) curstep = curstep->next;
                    curstep->next = kc->selector;
                }
                kc->selector = step;
            }
            return rc;
        case AxisDescendant:
            if (!toplevel) {
                SetResult ("Not a reduced XPath expression.");
                return 0;
            }
            nextType = SCHEMA_STEP_DESCENDANT_ELEMENT;
            break;
        case IsNSAttr:
        case IsAttr:
            if (!field) {
                SetResult ("Attribute selection is only possible in reduced "
                           "XPath expression for field selectors.");
                return 0;
            }
            /* Fall through */
        case IsElement:
        case IsFQElement:
        case IsNSElement:
            savedt = t;
            step = TMALLOC (KeyStep);
            memset (step, 0, sizeof (KeyStep));
            step->type = nextType;
            if (t->type == IsAttr || t->type == IsNSAttr) {
                table = &sdata->attrNames;
            } else {
                table = &sdata->element;
            }
            if (t->type == IsFQElement || t->type == IsNSAttr) {
                h = Tcl_CreateHashEntry (&sdata->namespace, t->strvalue,
                                         &hnew);
                step->ns = Tcl_GetHashKey (&sdata->namespace, h);
                t = t->child;
            }
            h = Tcl_CreateHashEntry (table, t->strvalue, &hnew);
            if (hnew && (table == &sdata->element)) {
                cp = initSchemaCP (SCHEMA_CTYPE_NAME, step->ns,
                                   Tcl_GetHashKey (&sdata->element, h));
                cp->flags |= PLACEHOLDER_PATTERN_DEF;
                REMEMBER_PATTERN (cp);
                Tcl_SetHashValue (h, cp);
            }
            step->name = Tcl_GetHashKey (table, h);
            if (field) {
                if (kc->fields[field-1]) {
                    curstep = kc->fields[field-1];
                    while (curstep->child) curstep = curstep->child;
                    curstep->child = step;
                } else {
                    kc->fields[field-1] = step;
                }
            } else {
                if (kc->selector) {
                    curstep = kc->selector;
                    while (curstep->child) curstep = curstep->child;
                    curstep->child = step;
                } else {
                    kc->selector = step;
                }
            }
            t = savedt;
            break;
        case AxisAttribute:
            if (!field) {
                SetResult ("Attribute selection is only possible in reduced "
                           "XPath expression for field selectors.");
                return 0;
            }
            /* Fall through */
        case AxisChild:
            if (t->type == AxisChild) nextType = SCHEMA_STEP_ELEMENT;
            else nextType = SCHEMA_STEP_ATTRIBUTE;
            if (!processSchemaXPath (interp, sdata, kc, nextType,
                                     t->child, field, 0)) {
                return 0;
            }
            break;
        default:
            SetResult ("Not a reduced XPath expression.");
            return 0;
        }
        toplevel = 0;
        t = t->next;
    }
    return 1;
}

static int
uniquePatternCmd (
    ClientData clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[]
    )
{
    SchemaData *sdata = GETASI;
    ast t;
    char *errMsg = NULL;
    KeyConstraint *kc, *kc1;
    int i, nrFields;
    Tcl_Obj *elm;

    CHECK_SI
    CHECK_TOPLEVEL
    checkNrArgs (3,4,"Expected: <selector> <fieldlist> ?<name>?");
    if (sdata->cp->type != SCHEMA_CTYPE_NAME) {
        SetResult ("The unique schema definition command is only "
                   "allowed as direct child of an element.");
    }
    
    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;
    }

    if (Tcl_ListObjLength (interp, objv[2], &nrFields) != TCL_OK) {
        SetResult ("The <fieldlist> argument must be a valid tcl list");
        xpathFreeAst (t);
        return TCL_ERROR;
    }
    if (nrFields == 0) {
        SetResult ("Non empty fieldlist arugment expected.");
        xpathFreeAst (t);
        return TCL_ERROR;
    }
    
    kc = TMALLOC (KeyConstraint);
    memset (kc, 0, sizeof (KeyConstraint));
    kc->fields = MALLOC (sizeof (KeyStep*) * nrFields);
    memset (kc->fields, 0, sizeof (KeyStep*) * nrFields);
    kc->nrFields = nrFields;
    
    if (!processSchemaXPath (interp, sdata, kc, SCHEMA_STEP_NONE, t,
                             0, 1)) {
        xpathFreeAst (t);
        freeKeyConstraints (kc);
        return TCL_ERROR;
    }
    xpathFreeAst (t);

    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);
            freeKeyConstraints (kc);
            return TCL_ERROR;
        }
        if (!processSchemaXPath (interp, sdata, kc, SCHEMA_STEP_NONE, t,
                                 i+1, 1)) {
            xpathFreeAst (t);
            freeKeyConstraints (kc);
            return TCL_ERROR;
        }
        xpathFreeAst (t);
    }
    if (objc == 4) {
        kc->name = tdomstrdup (Tcl_GetString (objv[3]));
    }
    if (sdata->cp->localkeys) {
        kc1 = sdata->cp->localkeys;
        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[]
5052
5053
5054
5055
5056
5057
5058



5059
5060
5061
5062
5063
5064
5065
    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);







>
>
>







5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
    Tcl_CreateObjCommand (interp, "tdom::schema::text",
                          TextPatternObjCmd, NULL, NULL);

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

Changes to generic/schema.h.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|   License for the specific language governing rights and limitations
|   under the License.
|
|   Contributor(s):
|
|
|   written by Rolf Ade
|   Nov, Dec 2018
|
\---------------------------------------------------------------------------*/

#ifndef __SCHEMA_H__
#define __SCHEMA_H__

#include <tcldom.h>







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|   License for the specific language governing rights and limitations
|   under the License.
|
|   Contributor(s):
|
|
|   written by Rolf Ade
|   2018-2019
|
\---------------------------------------------------------------------------*/

#ifndef __SCHEMA_H__
#define __SCHEMA_H__

#include <tcldom.h>
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

typedef enum {
  SCHEMA_CQUANT_ONE,
  SCHEMA_CQUANT_OPT,
  SCHEMA_CQUANT_REP,
  SCHEMA_CQUANT_PLUS,
  SCHEMA_CQUANT_NM,
  SCHEMA_CQUANT_ERROR,
} SchemaQuant;

typedef int (*SchemaConstraintFunc) (Tcl_Interp *interp,
                                     void *constraintData, char *text);
typedef void (*SchemaConstraintFreeFunc) (void *constraintData);

typedef struct 







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

typedef enum {
  SCHEMA_CQUANT_ONE,
  SCHEMA_CQUANT_OPT,
  SCHEMA_CQUANT_REP,
  SCHEMA_CQUANT_PLUS,
  SCHEMA_CQUANT_NM,
  SCHEMA_CQUANT_ERROR
} SchemaQuant;

typedef int (*SchemaConstraintFunc) (Tcl_Interp *interp,
                                     void *constraintData, char *text);
typedef void (*SchemaConstraintFreeFunc) (void *constraintData);

typedef struct 
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
108
109
110

111
112
113
114
115
116
117
    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;
    int               activeChild;
    int               hasMatched;
    int              *interleaveState;

} SchemaValidationStack;

typedef enum {
    VALIDATION_READY,
    VALIDATION_STARTED,
    VALIDATION_ERROR,
    VALIDATION_FINISHED







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













>











>







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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    ast    selector;
    ast   *fields;
    int    nrFields;
    int    flags;
    struct domKeyConstraint *next;
} domKeyConstraint;


typedef enum {
  SCHEMA_KEY_UNIQUE,
  SCHEMA_KEY_KEY,
  SCHEMA_KEY_KEYREF
} KeyType;

typedef enum {
  SCHEMA_STEP_NONE,
  SCHEMA_STEP_ELEMENT,
  SCHEMA_STEP_DESCENDANT_ELEMENT,
  SCHEMA_STEP_ATTRIBUTE,
  SCHEMA_STEP_WILDCARD,
  SCHEMA_STEP_NSWILDCARD,
} StepType;

typedef struct KeyStep 
{
    StepType type;
    char    *name;
    char    *ns;
    struct KeyStep *next;
    struct KeyStep *child;
} KeyStep;

typedef struct KeyConstraint {
    char     *name;
    KeyType   type;
    KeyStep  *selector;
    KeyStep **fields;
    int       nrFields;
    struct KeyConstraint *next;
} KeyConstraint;

typedef struct KeyTable {
    struct KeyTable *next;
    Tcl_HashTable    values;
} KeyTable;

typedef struct KeyState 
{
    struct KeyState *next;
    StepType         type;
    KeyStep         *selector;
    KeyStep        **fields;
    int              ownTable;
    KeyTable        *keyTable;
} KeyState;

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;
    KeyConstraint    *localkeys;
    domKeyConstraint *domKeys;
} SchemaCP;

typedef struct SchemaValidationStack
{
    SchemaCP *pattern;
    struct SchemaValidationStack *next;
    struct SchemaValidationStack *down;
    int               activeChild;
    int               hasMatched;
    int              *interleaveState;
    KeyState         *keyState;
} SchemaValidationStack;

typedef enum {
    VALIDATION_READY,
    VALIDATION_STARTED,
    VALIDATION_ERROR,
    VALIDATION_FINISHED
157
158
159
160
161
162
163


164
165
166
167
168
169
170
    unsigned int contentSize;
    SchemaAttr **currentAttrs;
    unsigned int numAttr;
    unsigned int numReqAttr;
    unsigned int attrSize;
    SchemaValidationStack *stack;
    SchemaValidationStack *stackPool;


    ValidationState validationState;
    unsigned int skipDeep;
    Tcl_DString *cdata;
    Tcl_HashTable ids;
    int unknownIDrefs;
    Tcl_HashTable idTables;
} SchemaData;







>
>







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    unsigned int contentSize;
    SchemaAttr **currentAttrs;
    unsigned int numAttr;
    unsigned int numReqAttr;
    unsigned int attrSize;
    SchemaValidationStack *stack;
    SchemaValidationStack *stackPool;
    KeyState *keyStatePool;
    KeyTable *keyTablePool;
    ValidationState validationState;
    unsigned int skipDeep;
    Tcl_DString *cdata;
    Tcl_HashTable ids;
    int unknownIDrefs;
    Tcl_HashTable idTables;
} SchemaData;

Changes to tests/schema.test.

592
593
594
595
596
597
598











599
600
601
602
603
604
605
        }
    } errMsg]
    lappend result $errMsg
    s delete
    set result
} {1 {Command only allowed at lop level} 1 {Command only allowed at lop level} 1 {Method not allowed in nested schema define script} 1 {This recursive call is not allowed}}












test schema-2.1 {grammar definition: ref} {
    tdom::schema create grammar
    grammar defpattern thisPattern {
        element a
        element b
    }
    grammar defpattern thatPattern {







>
>
>
>
>
>
>
>
>
>
>







592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
        }
    } errMsg]
    lappend result $errMsg
    s delete
    set result
} {1 {Command only allowed at lop level} 1 {Command only allowed at lop level} 1 {Method not allowed in nested schema define script} 1 {This recursive call is not allowed}}

test schema-1.27 {define} {
    tdom::schema create s
    set result ""
    s define {
        set ::result "from inside define"
        append ::result " " [expr {2+2}]
    }
    s delete
    set result
} {from inside define 4}
    
test schema-2.1 {grammar definition: ref} {
    tdom::schema create grammar
    grammar defpattern thisPattern {
        element a
        element b
    }
    grammar defpattern thatPattern {
4607
4608
4609
4610
4611
4612
4613






























































































































4614
4615
4616
4617
4618
4619
4620

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
        }







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







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
4753
4754
4755
4756
4757

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

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


test schema-19.2 {unique} {
    set schema {
        prefixns {ns1 http://tdom.org/test}
        defelement doc {
            unique ${::schema-19.2} @ref
        }
    }
    set result [list]
    foreach ::schema-19.2 {
        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/c
        (.//b|a)/c
        ns1:a
        a/.//b/c
        {}
        {    }
        "
                "
        ./item/./foo/.
        .
    } {
        tdom::schema s
        lappend result [catch {s define $schema}]
        s delete
    }
    set result
} {0 1 1 0 0 0 1 0 0 1 1 0 1 1 0 1 1 1 1 0 0}

test schema-19.2a {unique} {
    set schema {
        prefixns {ns1 http://tdom.org/test}
        defelement doc {
            unique ${::schema-19.2} {{@ref | div/@id} @lang}
        }
    }
    set result [list]
    foreach ::schema-19.2 {
        {e/b | a/b | e/f }
    } {
        tdom::schema s
        lappend result [catch {s define $schema}]
        s delete
    }
    set result
} 0

test schema-19.3 {unique} {
    tdom::schema s
    s define {
        defelement doc {
            unique item @lang
            element item * {
                attribute lang
            }
        }
    }
    set result [list]
    foreach xml {
        {<doc><item lang="de"/><item lang="en"/><item lang="fr"/></doc>}
    } {
        lappend result [s validate $xml]
    }
    s delete
    set result
} {1}

test schema-19.4 {unique} {
    tdom::schema s
    s define {
        defelement doc {
            unique item {@lang @sub} "Combined field unique constraint"
            element item * {
                attribute lang
            }
        }
    }
    set result [list]
    foreach xml {
        {<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
        }
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
        {<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}


}








|
<
4880
4881
4882
4883
4884
4885
4886
4887
4888

        {<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}

}