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 "PfError.hpp"
35 #include <algorithm>
36 #include <assert.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40
41 using std::string;
42
43 const std::string CElement::gDescriptionPropertyName = "Description";
44
CElement(const string & strName)45 CElement::CElement(const string &strName) : _strName(strName)
46 {
47 }
48
~CElement()49 CElement::~CElement()
50 {
51 removeChildren();
52 }
53
setDescription(const string & strDescription)54 void CElement::setDescription(const string &strDescription)
55 {
56 _strDescription = strDescription;
57 }
58
getDescription() const59 const string &CElement::getDescription() const
60 {
61 return _strDescription;
62 }
63
childrenAreDynamic() const64 bool CElement::childrenAreDynamic() const
65 {
66 // By default, children are searched and not created during xml parsing
67 return false;
68 }
69
init(string & strError)70 bool CElement::init(string &strError)
71 {
72
73 for (CElement *child : _childArray) {
74
75 if (!child->init(strError)) {
76
77 return false;
78 }
79 }
80
81 return true;
82 }
83
dumpContent(utility::ErrorContext & errorContext,const size_t depth) const84 string CElement::dumpContent(utility::ErrorContext &errorContext, const size_t depth) const
85 {
86 string output;
87 string strIndent;
88
89 // Level
90 size_t indents = depth;
91
92 while (indents--) {
93
94 strIndent += " ";
95 }
96 // Type
97 output += strIndent + "- " + getKind();
98
99 // Name
100 if (!_strName.empty()) {
101
102 output += ": " + getName();
103 }
104
105 // Value
106 string strValue = logValue(errorContext);
107
108 if (!strValue.empty()) {
109
110 output += " = " + strValue;
111 }
112
113 output += "\n";
114
115 for (CElement *pChild : _childArray) {
116
117 output += pChild->dumpContent(errorContext, depth + 1);
118 }
119
120 return output;
121 }
122
123 // Element properties
showProperties(string & strResult) const124 void CElement::showProperties(string &strResult) const
125 {
126 strResult += "Kind: " + getKind() + "\n";
127 showDescriptionProperty(strResult);
128 }
129
showDescriptionProperty(std::string & strResult) const130 void CElement::showDescriptionProperty(std::string &strResult) const
131 {
132 if (!getDescription().empty()) {
133 strResult += gDescriptionPropertyName + ": " + getDescription() + "\n";
134 }
135 }
136
137 // Content dumping
logValue(utility::ErrorContext &) const138 string CElement::logValue(utility::ErrorContext & /*ctx*/) const
139 {
140 return "";
141 }
142
143 // From IXmlSink
fromXml(const CXmlElement & xmlElement,CXmlSerializingContext & serializingContext)144 bool CElement::fromXml(const CXmlElement &xmlElement,
145 CXmlSerializingContext &serializingContext) try {
146 xmlElement.getAttribute(gDescriptionPropertyName, _strDescription);
147
148 // Propagate through children
149 CXmlElement::CChildIterator childIterator(xmlElement);
150
151 CXmlElement childElement;
152
153 while (childIterator.next(childElement)) {
154
155 CElement *pChild;
156
157 if (!childrenAreDynamic()) {
158
159 pChild = findChildOfKind(childElement.getType());
160
161 if (!pChild) {
162
163 serializingContext.setError("Unable to handle XML element: " +
164 childElement.getPath());
165
166 return false;
167 }
168
169 } else {
170 // Child needs creation
171 pChild = createChild(childElement, serializingContext);
172
173 if (!pChild) {
174
175 return false;
176 }
177 }
178
179 // Dig
180 if (!pChild->fromXml(childElement, serializingContext)) {
181
182 return false;
183 }
184 }
185
186 return true;
187 } catch (const PfError &e) {
188 serializingContext.appendLineToError(e.what());
189 return false;
190 }
191
childrenToXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const192 void CElement::childrenToXml(CXmlElement &xmlElement,
193 CXmlSerializingContext &serializingContext) const
194 {
195 // Browse children and propagate
196 for (CElement *pChild : _childArray) {
197
198 // Create corresponding child element
199 CXmlElement xmlChildElement;
200
201 xmlElement.createChild(xmlChildElement, pChild->getXmlElementName());
202
203 // Propagate
204 pChild->toXml(xmlChildElement, serializingContext);
205 }
206 }
207
toXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const208 void CElement::toXml(CXmlElement &xmlElement, CXmlSerializingContext &serializingContext) const
209 {
210 setXmlNameAttribute(xmlElement);
211 setXmlDescriptionAttribute(xmlElement);
212 childrenToXml(xmlElement, serializingContext);
213 }
214
setXmlDescriptionAttribute(CXmlElement & xmlElement) const215 void CElement::setXmlDescriptionAttribute(CXmlElement &xmlElement) const
216 {
217 const string &description = getDescription();
218 if (!description.empty()) {
219 xmlElement.setAttribute(gDescriptionPropertyName, description);
220 }
221 }
222
setXmlNameAttribute(CXmlElement & xmlElement) const223 void CElement::setXmlNameAttribute(CXmlElement &xmlElement) const
224 {
225 // By default, set Name attribute if any
226 string strName = getName();
227
228 if (!strName.empty()) {
229
230 xmlElement.setNameAttribute(strName);
231 }
232 }
233
234 // Name
setName(const string & strName)235 void CElement::setName(const string &strName)
236 {
237 _strName = strName;
238 }
239
getName() const240 const string &CElement::getName() const
241 {
242 return _strName;
243 }
244
rename(const string & strName,string & strError)245 bool CElement::rename(const string &strName, string &strError)
246 {
247 // Check for conflict with brotherhood if relevant
248 if (_pParent && _pParent->childrenAreDynamic()) {
249
250 for (CElement *pParentChild : _pParent->_childArray) {
251
252 if (pParentChild != this && pParentChild->getName() == strName) {
253
254 // Conflict
255 strError = "Name conflicts with brother element";
256
257 return false;
258 }
259 }
260 }
261 // Change name
262 setName(strName);
263
264 return true;
265 }
266
getPathName() const267 string CElement::getPathName() const
268 {
269 if (!_strName.empty()) {
270
271 return _strName;
272 } else {
273
274 return getKind();
275 }
276 }
277
278 // Hierarchy
addChild(CElement * pChild)279 void CElement::addChild(CElement *pChild)
280 {
281 _childArray.push_back(pChild);
282
283 pChild->_pParent = this;
284 }
285
getChild(size_t index)286 CElement *CElement::getChild(size_t index)
287 {
288 assert(index <= _childArray.size());
289
290 return _childArray[index];
291 }
292
getChild(size_t index) const293 const CElement *CElement::getChild(size_t index) const
294 {
295 assert(index <= _childArray.size());
296
297 return _childArray[index];
298 }
299
createChild(const CXmlElement & childElement,CXmlSerializingContext & serializingContext)300 CElement *CElement::createChild(const CXmlElement &childElement,
301 CXmlSerializingContext &serializingContext)
302 {
303 // Context
304 CXmlElementSerializingContext &elementSerializingContext =
305 static_cast<CXmlElementSerializingContext &>(serializingContext);
306
307 // Child needs creation
308 CElement *pChild = elementSerializingContext.getElementLibrary()->createElement(childElement);
309
310 if (!pChild) {
311
312 elementSerializingContext.setError("Unable to create XML element " +
313 childElement.getPath());
314
315 return nullptr;
316 }
317 // Store created child!
318 addChild(pChild);
319
320 return pChild;
321 }
322
removeChild(CElement * pChild)323 bool CElement::removeChild(CElement *pChild)
324 {
325 auto childIt = find(begin(_childArray), end(_childArray), pChild);
326 if (childIt != end(_childArray)) {
327
328 _childArray.erase(childIt);
329 return true;
330 }
331 return false;
332 }
333
listChildren(string & strChildList) const334 void CElement::listChildren(string &strChildList) const
335 {
336 // Get list of children names
337 for (CElement *pChild : _childArray) {
338
339 strChildList += pChild->getName() + "\n";
340 }
341 }
342
listQualifiedPaths(bool bDive,size_t level) const343 string CElement::listQualifiedPaths(bool bDive, size_t level) const
344 {
345 string strResult;
346
347 // Dive Will cause only leaf nodes to be printed
348 if (!bDive || !getNbChildren()) {
349
350 strResult = getQualifiedPath() + "\n";
351 }
352
353 if (bDive || !level) {
354 // Get list of children paths
355 for (CElement *pChild : _childArray) {
356
357 strResult += pChild->listQualifiedPaths(bDive, level + 1);
358 }
359 }
360 return strResult;
361 }
362
listChildrenPaths(string & strChildList) const363 void CElement::listChildrenPaths(string &strChildList) const
364 {
365 // Get list of children paths
366 for (CElement *pChild : _childArray) {
367
368 strChildList += pChild->getPath() + "\n";
369 }
370 }
371
getNbChildren() const372 size_t CElement::getNbChildren() const
373 {
374 return _childArray.size();
375 }
376
getParent() const377 const CElement *CElement::getParent() const
378 {
379 return _pParent;
380 }
381
getParent()382 CElement *CElement::getParent()
383 {
384 return _pParent;
385 }
386
clean()387 void CElement::clean()
388 {
389 if (childrenAreDynamic()) {
390
391 removeChildren();
392 } else {
393 // Just propagate
394 for (CElement *pChild : _childArray) {
395
396 pChild->clean();
397 }
398 }
399 }
400
removeChildren()401 void CElement::removeChildren()
402 {
403 // Delete in reverse order
404 ChildArrayReverseIterator it;
405
406 for (it = _childArray.rbegin(); it != _childArray.rend(); ++it) {
407
408 delete *it;
409 }
410 _childArray.clear();
411 }
412
findDescendant(CPathNavigator & pathNavigator) const413 const CElement *CElement::findDescendant(CPathNavigator &pathNavigator) const
414 {
415 string *pStrChildName = pathNavigator.next();
416
417 if (!pStrChildName) {
418
419 return this;
420 }
421
422 const CElement *pChild = findChild(*pStrChildName);
423
424 if (!pChild) {
425
426 return nullptr;
427 }
428
429 return pChild->findDescendant(pathNavigator);
430 }
431
findDescendant(CPathNavigator & pathNavigator)432 CElement *CElement::findDescendant(CPathNavigator &pathNavigator)
433 {
434 string *pStrChildName = pathNavigator.next();
435
436 if (!pStrChildName) {
437
438 return this;
439 }
440
441 CElement *pChild = findChild(*pStrChildName);
442
443 if (!pChild) {
444
445 return nullptr;
446 }
447
448 return pChild->findDescendant(pathNavigator);
449 }
450
isDescendantOf(const CElement * pCandidateAscendant) const451 bool CElement::isDescendantOf(const CElement *pCandidateAscendant) const
452 {
453 if (!_pParent) {
454
455 return false;
456 }
457 if (_pParent == pCandidateAscendant) {
458
459 return true;
460 }
461 return _pParent->isDescendantOf(pCandidateAscendant);
462 }
463
findChild(const string & strName)464 CElement *CElement::findChild(const string &strName)
465 {
466 for (CElement *pChild : _childArray) {
467
468 if (pChild->getPathName() == strName) {
469
470 return pChild;
471 }
472 }
473
474 return nullptr;
475 }
476
findChild(const string & strName) const477 const CElement *CElement::findChild(const string &strName) const
478 {
479 for (CElement *pChild : _childArray) {
480
481 if (pChild->getPathName() == strName) {
482
483 return pChild;
484 }
485 }
486
487 return nullptr;
488 }
489
findChildOfKind(const string & strKind)490 CElement *CElement::findChildOfKind(const string &strKind)
491 {
492 for (CElement *pChild : _childArray) {
493
494 if (pChild->getKind() == strKind) {
495
496 return pChild;
497 }
498 }
499
500 return nullptr;
501 }
502
findChildOfKind(const string & strKind) const503 const CElement *CElement::findChildOfKind(const string &strKind) const
504 {
505 for (CElement *pChild : _childArray) {
506
507 if (pChild->getKind() == strKind) {
508
509 return pChild;
510 }
511 }
512
513 return nullptr;
514 }
515
getPath() const516 string CElement::getPath() const
517 {
518 // Take out root element from the path
519 if (_pParent && _pParent->_pParent) {
520
521 return _pParent->getPath() + "/" + getPathName();
522 }
523 return "/" + getPathName();
524 }
525
getQualifiedPath() const526 string CElement::getQualifiedPath() const
527 {
528 return getPath() + " [" + getKind() + "]";
529 }
530
getXmlElementName() const531 string CElement::getXmlElementName() const
532 {
533 // Default to element kind
534 return getKind();
535 }
536