Coverage Report

Created: 2025-06-13 07:02

/src/xerces-c/src/xercesc/validators/common/AllContentModel.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/*
19
 * $Id: AllContentModel.cpp 676911 2008-07-15 13:27:32Z amassari $
20
 */
21
22
23
// ---------------------------------------------------------------------------
24
//  Includes
25
// ---------------------------------------------------------------------------
26
#include <xercesc/util/Janitor.hpp>
27
#include <xercesc/util/RuntimeException.hpp>
28
#include <xercesc/framework/XMLElementDecl.hpp>
29
#include <xercesc/framework/XMLValidator.hpp>
30
#include <xercesc/validators/common/ContentSpecNode.hpp>
31
#include <xercesc/validators/common/AllContentModel.hpp>
32
#include <xercesc/validators/schema/SubstitutionGroupComparator.hpp>
33
#include <xercesc/validators/schema/XercesElementWildcard.hpp>
34
35
XERCES_CPP_NAMESPACE_BEGIN
36
37
// ---------------------------------------------------------------------------
38
//  AllContentModel: Constructors and Destructor
39
// ---------------------------------------------------------------------------
40
AllContentModel::AllContentModel( ContentSpecNode* const parentContentSpec
41
                                , const bool             isMixed
42
                                , MemoryManager* const   manager) :
43
0
   fMemoryManager(manager)
44
0
 , fCount(0)
45
0
 , fChildren(0)
46
0
 , fChildOptional(0)
47
0
 , fNumRequired(0)
48
0
 , fIsMixed(isMixed)
49
0
 , fHasOptionalContent(false)
50
0
{
51
    //
52
    //  Create a vector of unsigned ints that will be filled in with the
53
    //  ids of the child nodes. It will be expanded as needed but we give
54
    //  it an initial capacity of 64 which should be more than enough for
55
    //  99% of the scenarios.
56
    //
57
58
0
    ValueVectorOf<QName*> children(64, fMemoryManager);
59
0
    ValueVectorOf<bool> childOptional(64, fMemoryManager);
60
61
    //
62
    //  Get the parent element's content spec. This is the head of the tree
63
    //  of nodes that describes the content model. We will iterate this
64
    //  tree.
65
    //
66
0
    ContentSpecNode* curNode = parentContentSpec;
67
0
    if (!curNode)
68
0
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_NoParentCSN, fMemoryManager);
69
70
    // And now call the private recursive method that iterates the tree
71
0
    if (curNode->getType() == ContentSpecNode::All
72
0
        && curNode->getMinOccurs() == 0) {
73
0
        fHasOptionalContent = true;
74
0
    }
75
0
    buildChildList(curNode, children, childOptional);
76
77
    //
78
    //  And now we know how many elements we need in our member list. So
79
    //  fill them in.
80
    //
81
0
    fCount = children.size();
82
0
    fChildren = (QName**) fMemoryManager->allocate(fCount * sizeof(QName*)); //new QName*[fCount];
83
0
    fChildOptional = (bool*) fMemoryManager->allocate(fCount * sizeof(bool)); //new bool[fCount];
84
0
    for (unsigned int index = 0; index < fCount; index++) {
85
0
        fChildren[index] = new (fMemoryManager) QName(*(children.elementAt(index)));
86
0
        fChildOptional[index] = childOptional.elementAt(index);
87
0
    }
88
0
}
89
90
AllContentModel::~AllContentModel()
91
0
{
92
0
    for (XMLSize_t index = 0; index < fCount; index++)
93
0
        delete fChildren[index];
94
0
    fMemoryManager->deallocate(fChildren); //delete [] fChildren;
95
0
    fMemoryManager->deallocate(fChildOptional); //delete [] fChildOptional;
96
0
}
97
98
// ---------------------------------------------------------------------------
99
//  AllContentModel: Implementation of the ContentModel virtual interface
100
// ---------------------------------------------------------------------------
101
//
102
//Under the XML Schema mixed model,
103
//the order and number of child elements appearing in an instance
104
//must agree with
105
//the order and number of child elements specified in the model.
106
//
107
bool
108
AllContentModel::validateContent( QName** const         children
109
                                , XMLSize_t             childCount
110
                                , unsigned int
111
                                , XMLSize_t*            indexFailingChild
112
                                , MemoryManager*    const manager) const
113
0
{
114
    // If <all> had minOccurs of zero and there are
115
    // no children to validate, trivially validate
116
0
    if (childCount == 0 && (fHasOptionalContent || !fNumRequired))
117
0
        return true;
118
119
    // keep track of the required element seen
120
0
    XMLSize_t numRequiredSeen = 0;
121
122
0
    if(childCount > 0)
123
0
    {        
124
        // Check for duplicate element
125
0
        bool* elementSeen = (bool*) manager->allocate(fCount*sizeof(bool)); //new bool[fCount];
126
127
0
        const ArrayJanitor<bool> jan(elementSeen, manager);
128
129
        // initialize the array
130
0
        for (XMLSize_t i = 0; i < fCount; i++)
131
0
            elementSeen[i] = false;
132
133
0
        for (XMLSize_t outIndex = 0; outIndex < childCount; outIndex++) {
134
            // Get the current child out of the source index
135
0
            const QName* curChild = children[outIndex];
136
137
            // If it's PCDATA, then we just accept that
138
0
            if (fIsMixed && curChild->getURI() == XMLElementDecl::fgPCDataElemId)
139
0
                continue;
140
141
            // And try to find it in our list
142
0
            XMLSize_t inIndex = 0;
143
0
            for (; inIndex < fCount; inIndex++)
144
0
            {
145
0
                const QName* inChild = fChildren[inIndex];
146
0
                if ((inChild->getURI() == curChild->getURI()) &&
147
0
                    (XMLString::equals(inChild->getLocalPart(), curChild->getLocalPart()))) {
148
                    // found it
149
                    // If this element was seen already, indicate an error was
150
                    // found at the duplicate index.
151
0
                    if (elementSeen[inIndex]) {
152
0
                        *indexFailingChild=outIndex;
153
0
                        return false;
154
0
                    }
155
0
                    else
156
0
                        elementSeen[inIndex] = true;
157
158
0
                    if (!fChildOptional[inIndex])
159
0
                        numRequiredSeen++;
160
161
0
                    break;
162
0
                }
163
0
            }
164
165
            // We did not find this one, so the validation failed
166
0
            if (inIndex == fCount) {
167
0
                *indexFailingChild=outIndex;
168
0
                return false;
169
0
            }
170
171
0
        }
172
0
    }
173
174
    // Were all the required elements of the <all> encountered?
175
0
    if (numRequiredSeen != fNumRequired) {
176
0
        *indexFailingChild=childCount;
177
0
        return false;
178
0
    }
179
180
    // Everything seems to be ok, so return success
181
0
    return true;
182
0
}
183
184
185
bool AllContentModel::validateContentSpecial(QName** const          children
186
                                          , XMLSize_t               childCount
187
                                          , unsigned int
188
                                          , GrammarResolver*  const pGrammarResolver
189
                                          , XMLStringPool*    const pStringPool
190
                                          , XMLSize_t*              indexFailingChild
191
                                          , MemoryManager*    const manager) const
192
0
{
193
    // If <all> had minOccurs of zero and there are
194
    // no children to validate, trivially validate
195
0
    if (childCount == 0 && (fHasOptionalContent || !fNumRequired))
196
0
        return true;
197
198
    // keep track of the required element seen
199
0
    XMLSize_t numRequiredSeen = 0;
200
201
0
    if(childCount > 0)
202
0
    {
203
0
        SubstitutionGroupComparator comparator(pGrammarResolver, pStringPool);
204
205
        // Check for duplicate element
206
0
        bool* elementSeen = (bool*) manager->allocate(fCount*sizeof(bool)); //new bool[fCount];
207
208
0
        const ArrayJanitor<bool> jan(elementSeen, manager);
209
210
        // initialize the array
211
0
        for (XMLSize_t i = 0; i < fCount; i++)
212
0
            elementSeen[i] = false;
213
214
0
        for (XMLSize_t outIndex = 0; outIndex < childCount; outIndex++) {
215
            // Get the current child out of the source index
216
0
            QName* const curChild = children[outIndex];
217
218
            // If it's PCDATA, then we just accept that
219
0
            if (fIsMixed && curChild->getURI() == XMLElementDecl::fgPCDataElemId)
220
0
                continue;
221
222
            // And try to find it in our list
223
0
            XMLSize_t inIndex = 0;
224
0
            for (; inIndex < fCount; inIndex++)
225
0
            {
226
0
                QName* const inChild = fChildren[inIndex];
227
0
                if ( comparator.isEquivalentTo(curChild, inChild)) {
228
                    // match
229
                    // If this element was seen already, indicate an error was
230
                    // found at the duplicate index.
231
0
                    if (elementSeen[inIndex]) {
232
0
                        *indexFailingChild=outIndex;
233
0
                        return false;
234
0
                    }
235
0
                    else
236
0
                        elementSeen[inIndex] = true;
237
238
0
                    if (!fChildOptional[inIndex])
239
0
                        numRequiredSeen++;
240
241
0
                    break;
242
0
                }
243
0
            }
244
245
            // We did not find this one, so the validation failed
246
0
            if (inIndex == fCount) {
247
0
                *indexFailingChild=outIndex;
248
0
                return false;
249
0
            }
250
251
0
        }
252
0
    }
253
254
    // Were all the required elements of the <all> encountered?
255
0
    if (numRequiredSeen != fNumRequired) {
256
0
        *indexFailingChild=childCount;
257
0
        return false;
258
0
    }
259
260
    // Everything seems to be ok, so return success
261
0
    return true;
262
263
0
}
264
265
void AllContentModel::checkUniqueParticleAttribution
266
    (
267
        SchemaGrammar*    const pGrammar
268
      , GrammarResolver*  const pGrammarResolver
269
      , XMLStringPool*    const pStringPool
270
      , XMLValidator*     const pValidator
271
      , unsigned int*     const pContentSpecOrgURI
272
      , const XMLCh*            pComplexTypeName /*= 0*/
273
    )
274
0
{
275
0
    SubstitutionGroupComparator comparator(pGrammarResolver, pStringPool);
276
277
0
    XMLSize_t i, j;
278
279
    // rename back
280
0
    for (i = 0; i < fCount; i++) {
281
0
        unsigned int orgURIIndex = fChildren[i]->getURI();
282
0
        fChildren[i]->setURI(pContentSpecOrgURI[orgURIIndex]);
283
0
    }
284
285
    // check whether there is conflict between any two leaves
286
0
    for (i = 0; i < fCount; i++) {
287
0
        for (j = i+1; j < fCount; j++) {
288
            // If this is text in a Schema mixed content model, skip it.
289
0
            if ( fIsMixed &&
290
0
                 (( fChildren[i]->getURI() == XMLElementDecl::fgPCDataElemId) ||
291
0
                  ( fChildren[j]->getURI() == XMLElementDecl::fgPCDataElemId)))
292
0
                continue;
293
294
0
            if (XercesElementWildcard::conflict(pGrammar,
295
0
                                                ContentSpecNode::Leaf,
296
0
                                                fChildren[i],
297
0
                                                ContentSpecNode::Leaf,
298
0
                                                fChildren[j],
299
0
                                                &comparator)) {
300
0
                pValidator->emitError(XMLValid::UniqueParticleAttributionFail,
301
0
                                      pComplexTypeName,
302
0
                                      fChildren[i]->getRawName(),
303
0
                                      fChildren[j]->getRawName());
304
0
             }
305
0
         }
306
0
    }
307
0
}
308
309
// ---------------------------------------------------------------------------
310
//  AllContentModel: Private helper methods
311
// ---------------------------------------------------------------------------
312
void
313
AllContentModel::buildChildList(ContentSpecNode* const       curNode
314
                              , ValueVectorOf<QName*>&       toFill
315
                              , ValueVectorOf<bool>&         toOptional)
316
0
{
317
    // Get the type of spec node our current node is
318
0
    const ContentSpecNode::NodeTypes curType = curNode->getType();
319
320
0
    if (curType == ContentSpecNode::All)
321
0
    {
322
        // Get both the child node pointers
323
0
        ContentSpecNode* leftNode = curNode->getFirst();
324
0
        ContentSpecNode* rightNode = curNode->getSecond();
325
326
        // Recurse on the left and right nodes
327
0
        buildChildList(leftNode, toFill, toOptional);
328
0
        if(rightNode)
329
0
            buildChildList(rightNode, toFill, toOptional);
330
0
    }
331
0
    else if (curType == ContentSpecNode::Leaf)
332
0
    {
333
        // At leaf, add the element to list of elements permitted in the all
334
0
        toFill.addElement(curNode->getElement());
335
0
        toOptional.addElement(false);
336
0
        fNumRequired++;
337
0
    }
338
0
    else if (curType == ContentSpecNode::ZeroOrOne)
339
0
    {
340
        // At ZERO_OR_ONE node, subtree must be an element
341
        // that was specified with minOccurs=0, maxOccurs=1
342
0
        ContentSpecNode* leftNode = curNode->getFirst();
343
0
        if (leftNode->getType() != ContentSpecNode::Leaf)
344
0
            ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_UnknownCMSpecType, fMemoryManager);
345
346
0
        toFill.addElement(leftNode->getElement());
347
0
        toOptional.addElement(true);
348
0
    }
349
    // only allow ZeroOrMore when it's the father of a Loop
350
0
    else if (curType == ContentSpecNode::ZeroOrMore &&
351
0
             curNode->getFirst()!=0 &&
352
0
             curNode->getFirst()->getType()==ContentSpecNode::Loop)
353
0
    {
354
0
        ContentSpecNode* leftNode = curNode->getFirst();
355
0
        buildChildList(leftNode, toFill, toOptional);
356
0
    }
357
0
    else if (curType == ContentSpecNode::Loop)
358
0
    {
359
        // At leaf, add the element to list of elements permitted in the all
360
0
        int i;
361
0
        for(i=0;i<curNode->getMinOccurs();i++)
362
0
        {
363
0
            toFill.addElement(curNode->getElement());
364
0
            toOptional.addElement(false);
365
0
            fNumRequired++;
366
0
        }
367
0
        if(curNode->getMaxOccurs()!=-1)
368
0
            for(i=0;i<(curNode->getMaxOccurs() - curNode->getMinOccurs());i++)
369
0
            {
370
0
                toFill.addElement(curNode->getElement());
371
0
                toOptional.addElement(true);
372
0
            }
373
0
    }
374
0
    else
375
0
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_UnknownCMSpecType, fMemoryManager);
376
0
}
377
378
XERCES_CPP_NAMESPACE_END