1 /*
2 * Copyright (c) 2011-2015, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 #include "Element.h"
31 #include "XmlElementSerializingContext.h"
32 #include "ElementLibrary.h"
33 #include "ErrorContext.hpp"
34 #include <algorithm>
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <stdlib.h>
39
40 using std::string;
41
42 const std::string CElement::gDescriptionPropertyName = "Description";
43
CElement(const string & strName)44 CElement::CElement(const string &strName) : _strName(strName)
45 {
46 }
47
~CElement()48 CElement::~CElement()
49 {
50 removeChildren();
51 }
52
setDescription(const string & strDescription)53 void CElement::setDescription(const string &strDescription)
54 {
55 _strDescription = strDescription;
56 }
57
getDescription() const58 const string &CElement::getDescription() const
59 {
60 return _strDescription;
61 }
62
childrenAreDynamic() const63 bool CElement::childrenAreDynamic() const
64 {
65 // By default, children are searched and not created during xml parsing
66 return false;
67 }
68
init(string & strError)69 bool CElement::init(string &strError)
70 {
71
72 for (CElement *child : _childArray) {
73
74 if (!child->init(strError)) {
75
76 return false;
77 }
78 }
79
80 return true;
81 }
82
dumpContent(utility::ErrorContext & errorContext,const size_t depth) const83 string CElement::dumpContent(utility::ErrorContext &errorContext, const size_t depth) const
84 {
85 string output;
86 string strIndent;
87
88 // Level
89 size_t indents = depth;
90
91 while (indents--) {
92
93 strIndent += " ";
94 }
95 // Type
96 output += strIndent + "- " + getKind();
97
98 // Name
99 if (!_strName.empty()) {
100
101 output += ": " + getName();
102 }
103
104 // Value
105 string strValue = logValue(errorContext);
106
107 if (!strValue.empty()) {
108
109 output += " = " + strValue;
110 }
111
112 output += "\n";
113
114 for (CElement *pChild : _childArray) {
115
116 output += pChild->dumpContent(errorContext, depth + 1);
117 }
118
119 return output;
120 }
121
122 // Element properties
showProperties(string & strResult) const123 void CElement::showProperties(string &strResult) const
124 {
125 strResult += "Kind: " + getKind() + "\n";
126 showDescriptionProperty(strResult);
127 }
128
showDescriptionProperty(std::string & strResult) const129 void CElement::showDescriptionProperty(std::string &strResult) const
130 {
131 if (!getDescription().empty()) {
132 strResult += gDescriptionPropertyName + ": " + getDescription() + "\n";
133 }
134 }
135
136 // Content dumping
logValue(utility::ErrorContext &) const137 string CElement::logValue(utility::ErrorContext & /*ctx*/) const
138 {
139 return "";
140 }
141
142 // From IXmlSink
fromXml(const CXmlElement & xmlElement,CXmlSerializingContext & serializingContext)143 bool CElement::fromXml(const CXmlElement &xmlElement, CXmlSerializingContext &serializingContext)
144 {
145 xmlElement.getAttribute(gDescriptionPropertyName, _strDescription);
146
147 // Propagate through children
148 CXmlElement::CChildIterator childIterator(xmlElement);
149
150 CXmlElement childElement;
151
152 while (childIterator.next(childElement)) {
153
154 CElement *pChild;
155
156 if (!childrenAreDynamic()) {
157
158 pChild = findChildOfKind(childElement.getType());
159
160 if (!pChild) {
161
162 serializingContext.setError("Unable to handle XML element: " +
163 childElement.getPath());
164
165 return false;
166 }
167
168 } else {
169 // Child needs creation
170 pChild = createChild(childElement, serializingContext);
171
172 if (!pChild) {
173
174 return false;
175 }
176 }
177
178 // Dig
179 if (!pChild->fromXml(childElement, serializingContext)) {
180
181 return false;
182 }
183 }
184
185 return true;
186 }
187
childrenToXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const188 void CElement::childrenToXml(CXmlElement &xmlElement,
189 CXmlSerializingContext &serializingContext) const
190 {
191 // Browse children and propagate
192 for (CElement *pChild : _childArray) {
193
194 // Create corresponding child element
195 CXmlElement xmlChildElement;
196
197 xmlElement.createChild(xmlChildElement, pChild->getXmlElementName());
198
199 // Propagate
200 pChild->toXml(xmlChildElement, serializingContext);
201 }
202 }
203
toXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const204 void CElement::toXml(CXmlElement &xmlElement, CXmlSerializingContext &serializingContext) const
205 {
206 setXmlNameAttribute(xmlElement);
207 setXmlDescriptionAttribute(xmlElement);
208 childrenToXml(xmlElement, serializingContext);
209 }
210
setXmlDescriptionAttribute(CXmlElement & xmlElement) const211 void CElement::setXmlDescriptionAttribute(CXmlElement &xmlElement) const
212 {
213 const string &description = getDescription();
214 if (!description.empty()) {
215 xmlElement.setAttribute(gDescriptionPropertyName, description);
216 }
217 }
218
setXmlNameAttribute(CXmlElement & xmlElement) const219 void CElement::setXmlNameAttribute(CXmlElement &xmlElement) const
220 {
221 // By default, set Name attribute if any
222 string strName = getName();
223
224 if (!strName.empty()) {
225
226 xmlElement.setNameAttribute(strName);
227 }
228 }
229
230 // Name
setName(const string & strName)231 void CElement::setName(const string &strName)
232 {
233 _strName = strName;
234 }
235
getName() const236 const string &CElement::getName() const
237 {
238 return _strName;
239 }
240
rename(const string & strName,string & strError)241 bool CElement::rename(const string &strName, string &strError)
242 {
243 // Check for conflict with brotherhood if relevant
244 if (_pParent && _pParent->childrenAreDynamic()) {
245
246 for (CElement *pParentChild : _pParent->_childArray) {
247
248 if (pParentChild != this && pParentChild->getName() == strName) {
249
250 // Conflict
251 strError = "Name conflicts with brother element";
252
253 return false;
254 }
255 }
256 }
257 // Change name
258 setName(strName);
259
260 return true;
261 }
262
getPathName() const263 string CElement::getPathName() const
264 {
265 if (!_strName.empty()) {
266
267 return _strName;
268 } else {
269
270 return getKind();
271 }
272 }
273
274 // Hierarchy
addChild(CElement * pChild)275 void CElement::addChild(CElement *pChild)
276 {
277 _childArray.push_back(pChild);
278
279 pChild->_pParent = this;
280 }
281
getChild(size_t index)282 CElement *CElement::getChild(size_t index)
283 {
284 assert(index <= _childArray.size());
285
286 return _childArray[index];
287 }
288
getChild(size_t index) const289 const CElement *CElement::getChild(size_t index) const
290 {
291 assert(index <= _childArray.size());
292
293 return _childArray[index];
294 }
295
createChild(const CXmlElement & childElement,CXmlSerializingContext & serializingContext)296 CElement *CElement::createChild(const CXmlElement &childElement,
297 CXmlSerializingContext &serializingContext)
298 {
299 // Context
300 CXmlElementSerializingContext &elementSerializingContext =
301 static_cast<CXmlElementSerializingContext &>(serializingContext);
302
303 // Child needs creation
304 CElement *pChild = elementSerializingContext.getElementLibrary()->createElement(childElement);
305
306 if (!pChild) {
307
308 elementSerializingContext.setError("Unable to create XML element " +
309 childElement.getPath());
310
311 return NULL;
312 }
313 // Store created child!
314 addChild(pChild);
315
316 return pChild;
317 }
318
removeChild(CElement * pChild)319 bool CElement::removeChild(CElement *pChild)
320 {
321 auto childIt = find(begin(_childArray), end(_childArray), pChild);
322 if (childIt != end(_childArray)) {
323
324 _childArray.erase(childIt);
325 return true;
326 }
327 return false;
328 }
329
listChildren(string & strChildList) const330 void CElement::listChildren(string &strChildList) const
331 {
332 // Get list of children names
333 for (CElement *pChild : _childArray) {
334
335 strChildList += pChild->getName() + "\n";
336 }
337 }
338
listQualifiedPaths(bool bDive,size_t level) const339 string CElement::listQualifiedPaths(bool bDive, size_t level) const
340 {
341 string strResult;
342
343 // Dive Will cause only leaf nodes to be printed
344 if (!bDive || !getNbChildren()) {
345
346 strResult = getQualifiedPath() + "\n";
347 }
348
349 if (bDive || !level) {
350 // Get list of children paths
351 for (CElement *pChild : _childArray) {
352
353 strResult += pChild->listQualifiedPaths(bDive, level + 1);
354 }
355 }
356 return strResult;
357 }
358
listChildrenPaths(string & strChildList) const359 void CElement::listChildrenPaths(string &strChildList) const
360 {
361 // Get list of children paths
362 for (CElement *pChild : _childArray) {
363
364 strChildList += pChild->getPath() + "\n";
365 }
366 }
367
getNbChildren() const368 size_t CElement::getNbChildren() const
369 {
370 return _childArray.size();
371 }
372
getParent() const373 const CElement *CElement::getParent() const
374 {
375 return _pParent;
376 }
377
getParent()378 CElement *CElement::getParent()
379 {
380 return _pParent;
381 }
382
clean()383 void CElement::clean()
384 {
385 if (childrenAreDynamic()) {
386
387 removeChildren();
388 } else {
389 // Just propagate
390 for (CElement *pChild : _childArray) {
391
392 pChild->clean();
393 }
394 }
395 }
396
removeChildren()397 void CElement::removeChildren()
398 {
399 // Delete in reverse order
400 ChildArrayReverseIterator it;
401
402 for (it = _childArray.rbegin(); it != _childArray.rend(); ++it) {
403
404 delete *it;
405 }
406 _childArray.clear();
407 }
408
findDescendant(CPathNavigator & pathNavigator) const409 const CElement *CElement::findDescendant(CPathNavigator &pathNavigator) const
410 {
411 string *pStrChildName = pathNavigator.next();
412
413 if (!pStrChildName) {
414
415 return this;
416 }
417
418 const CElement *pChild = findChild(*pStrChildName);
419
420 if (!pChild) {
421
422 return NULL;
423 }
424
425 return pChild->findDescendant(pathNavigator);
426 }
427
findDescendant(CPathNavigator & pathNavigator)428 CElement *CElement::findDescendant(CPathNavigator &pathNavigator)
429 {
430 string *pStrChildName = pathNavigator.next();
431
432 if (!pStrChildName) {
433
434 return this;
435 }
436
437 CElement *pChild = findChild(*pStrChildName);
438
439 if (!pChild) {
440
441 return NULL;
442 }
443
444 return pChild->findDescendant(pathNavigator);
445 }
446
isDescendantOf(const CElement * pCandidateAscendant) const447 bool CElement::isDescendantOf(const CElement *pCandidateAscendant) const
448 {
449 if (!_pParent) {
450
451 return false;
452 }
453 if (_pParent == pCandidateAscendant) {
454
455 return true;
456 }
457 return _pParent->isDescendantOf(pCandidateAscendant);
458 }
459
findChild(const string & strName)460 CElement *CElement::findChild(const string &strName)
461 {
462 for (CElement *pChild : _childArray) {
463
464 if (pChild->getPathName() == strName) {
465
466 return pChild;
467 }
468 }
469
470 return NULL;
471 }
472
findChild(const string & strName) const473 const CElement *CElement::findChild(const string &strName) const
474 {
475 for (CElement *pChild : _childArray) {
476
477 if (pChild->getPathName() == strName) {
478
479 return pChild;
480 }
481 }
482
483 return NULL;
484 }
485
findChildOfKind(const string & strKind)486 CElement *CElement::findChildOfKind(const string &strKind)
487 {
488 for (CElement *pChild : _childArray) {
489
490 if (pChild->getKind() == strKind) {
491
492 return pChild;
493 }
494 }
495
496 return NULL;
497 }
498
findChildOfKind(const string & strKind) const499 const CElement *CElement::findChildOfKind(const string &strKind) const
500 {
501 for (CElement *pChild : _childArray) {
502
503 if (pChild->getKind() == strKind) {
504
505 return pChild;
506 }
507 }
508
509 return NULL;
510 }
511
getPath() const512 string CElement::getPath() const
513 {
514 // Take out root element from the path
515 if (_pParent && _pParent->_pParent) {
516
517 return _pParent->getPath() + "/" + getPathName();
518 }
519 return "/" + getPathName();
520 }
521
getQualifiedPath() const522 string CElement::getQualifiedPath() const
523 {
524 return getPath() + " [" + getKind() + "]";
525 }
526
getXmlElementName() const527 string CElement::getXmlElementName() const
528 {
529 // Default to element kind
530 return getKind();
531 }
532