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